【问题标题】:Point Free problems in HaskellHaskell 中的无点问题
【发布时间】:2015-04-13 00:33:57
【问题描述】:

我正在尝试将以下 Haskell 代码转换为无点样式,但无济于事。

bar f g xs = filter f (map g xs )

我是 Haskell 的新手,任何帮助都会很棒。

【问题讨论】:

  • 请注意,在某些情况下(像这样),无点版本的可读性可能比原始代码低得多。除非您的目标是混淆代码,否则请谨慎使用无点样式。

标签: haskell pointfree


【解决方案1】:

转换为无点样式可以完全机械地完成,尽管如果不熟悉 Haskell 语法的基本原理(如左关联函数应用程序和 x + y(+) x y 相同)则很难。我假设您对 Haskell 语法感到满意;如果没有,我建议先阅读LYAH 的前几章。

您需要标准库中的以下组合子。我还从组合微积分中给出了它们的标准名称。

id :: a -> a                                   -- I
const :: a -> b -> a                           -- K
(.) :: (b -> c) -> (a -> b) -> (a -> c)        -- B
flip :: (a -> b -> c) -> (b -> a -> c)         -- C
(<*>) :: (a -> b -> c) -> (a -> b) -> (a -> c) -- S

一次使用一个参数。将左侧的参数移动到右侧的 lambdas,例如

f x y = Z

变成

f = \x -> \y -> Z

我喜欢一次做一个参数而不是一次做所有参数,这样看起来更干净。

然后根据以下规则消除你刚刚创建的 lambda。我将使用小写字母表示文字变量,大写字母表示更复杂的表达式。

  1. 如果您有\x -&gt; x,请替换为id
  2. 如果您有\x -&gt; A,其中A 是没有出现x 的任何表达式,请替换为const A
  3. 如果您有\x -&gt; A x,其中x 不会出现在A 中,请替换为A。这称为“eta 收缩”。
  4. 如果你有\x -&gt; A B,那么
    1. 如果x 出现在AB 中,则替换为(\x -&gt; A) &lt;*&gt; (\x -&gt; B)
    2. 如果x 仅出现在A 中,则替换为flip (\x -&gt; A) B
    3. 如果x仅出现在B中,则替换为A . (\x -&gt; B)
    4. 如果x 没有出现在AB 中,那么我们应该已经使用了另一个规则。

然后向内工作,消除您创建的 lambda。让我们来看看这个例子:

f x y z = foo z (bar x y)
-- Move parameter to lambda:
f x y = \z -> foo z (bar x y)
-- Remember that application is left-associative, so this is the same as
f x y = \z -> (foo z) (bar x y)
-- z appears on the left and not on the right, use flip
f x y = flip (\z -> foo z) (bar x y)
-- Use rule (3) 
f x y = flip foo (bar x y)

-- Next parameter
f x = \y -> flip foo (bar x y)
-- Application is left-associative
f x = \y -> (flip foo) (bar x y)
-- y occurs on the right but not the left, use (.)
f x = flip foo . (\y -> bar x y)
-- Use rule 3
f x = flip foo . bar x

-- Next parameter
f = \x -> flip foo . bar x
-- We need to rewrite this operator into normal application style
f = \x -> (.) (flip foo) (bar x)
-- Application is left-associative
f = \x -> ((.) (flip foo)) (bar x)
-- x appears on the right but not the left, use (.)
f = ((.) (flip foo)) . (\x -> bar x)
-- use rule (3)
f = ((.) (flip foo)) . bar
-- Redundant parentheses
f = (.) (flip foo) . bar

你去,现在试试你的!在决定使用哪条规则时,实际上并没有任何聪明之处:使用任何适用的规则,你就会取得进步。

【讨论】:

  • 这是我第一次看到有人发布实际的算法来做这件事。我知道肯定有一个,但我不知道它到底是什么......
  • 我看过几本教科书很难像您所做的那样清楚地呈现转换! (顺便说一句,最后一个表达式通常被视为(flip foo .) . bar)。
  • 作为参考,我想补充一点,这(大部分)是converting from lambda calculus into SK calculus(以防万一有人想用谷歌搜索更多信息)。
  • “使用任何适用的规则”如果要应用多个规则,它会引入不确定性和做出“最佳”选择的“聪明”。例如,您在此处排除了 Wxy = xyy。
【解决方案2】:

现有的两个答案都没有真正以明确的方式回答您的具体问题:一个是“这里是规则,你自己解决”,另一个是“这里是答案,没有关于如何做的信息规则生成它。”

前三个步骤非常简单,包括通过编写h = f . gh x = f (g x) 形式的内容中删除常见的x。本质上它是在说“如果你可以以a $ b $ c $ ... $ y $ z 的形式写东西并且你想删除z,把所有的美元都改成点,a . b . c . ... . y:

bar f g xs = filter f (map g xs)
           = filter f $ (map g xs)
           = filter f $ map g $ xs -- because a $ b $ c == a $ (b $ c).
bar f g    = filter f . map g
           = (filter f .) (map g)
           = (filter f .) $ map $ g
bar f      = (filter f .) . map

所以最后一个f 是唯一棘手的部分,而且这很棘手,因为f 不在表达式的“末尾”。但仔细看,我们看到这是一个函数部分 (. map) 应用于表达式的其余部分:

bar f = (.) (filter f) . map
bar f = (. map) $ (.) $ filter $ f
bar   = (. map) . (.) . filter

这就是当你没有像f x x 之类的复杂事物出现在表达式中时减少表达式的方法。通常有一个函数flip f x y = f y x 可以“翻转参数”;您始终可以使用它将f 移动到另一侧。如果您包含显式翻转调用,我们这里有flip (.) map . (.) . filter

【讨论】:

    【解决方案3】:

    我要求 lambdabot,一个在各种 Haskell IRC 频道上闲逛的机器人,自动计算出无积分等价物。命令是@pl毫无意义)。

    10:41 <frase> @pl bar f g xs = filter f (map g xs )
    10:41 <lambdabot> bar = (. map) . (.) . filter
    

    bar的免积分版是:

    bar = (. map) . (.) . filter
    

    这可以说比原始(非无点)代码更难理解。根据具体情况决定是否使用无点样式时,请运用您的良好判断力。

    最后,如果你不关心 IRC,还有基于 web 的免点 转换器,例如pointfree.iopointfree 命令行程序和其他工具。

    【讨论】:

    • 如果您不想启动 IRC 客户端,我编写了一个 Web 服务来为您完成这些转换。它被称为 Blunt:blunt.herokuapp.com/…
    • 您也可以使用cabal install pointfree安装命令行版本。如果你给它自己的 ghci 定义,你可以从 ghci 中使用它:) hackage.haskell.org/package/pointfree
    • @TaylorFausak,Blunt 是死了,还是刚搬家?
    • @dfeuer 它已经死了。
    猜你喜欢
    • 2013-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-27
    • 2012-03-23
    • 1970-01-01
    • 2019-06-03
    • 1970-01-01
    相关资源
    最近更新 更多