Haskell中的Point Free问题

时间:2015-04-13 00:33:57

标签: haskell

我正在尝试将以下haskell代码转换为指向自由样式,但没有用。

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

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

3 个答案:

答案 0 :(得分:45)

转换为pointfree样式可以完全机械地完成,但是如果不熟悉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

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

f x y = Z

变为

f = \x -> \y -> Z

我喜欢一次做一个这样的论点而不是一次性,它看起来更干净。

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

  1. 如果您有\x -> x,请替换为id
  2. 如果您有\x -> A,其中Ax未出现const A的任何表达式,请替换为\x -> A x
  3. 如果您x AA中未\x -> A B,则替换为x。这被称为&#34; eta contraction&#34;。
  4. 如果你有A,那么
    1. 如果B(\x -> A) <*> (\x -> B)同时发生x,请替换为A
    2. 如果仅flip (\x -> A) B发生x,请替换为B
    3. 如果仅A . (\x -> B)发生x,请替换为A
    4. 如果Bf 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 中没有出现{{1}},那么我们应该已经使用了另一条规则。
  5. 然后向内工作,消除你创造的lambda。让我们使用这个例子:

    {{1}}

    你去了,现在试试吧!决定使用哪条规则并不是很聪明:使用适用的任何规则,您将取得进展。

答案 1 :(得分:7)

现有的两个答案都没有以一种明确的方式回答你的具体问题:一个是&#34;这是规则,为自己解决问题&#34;另一个是&#34;这里是答案,没有关于规则如何生成它的信息。&#34;

前三个步骤非常简单,包括通过编写xh x = f (g x)形式的内容中删除常见的h = f . g。基本上它是在说#34;如果你可以用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不在&#34;结束&#34;的表达。但是看一下,我们看到这是一个应用于表达式其余部分的函数部分(. map)

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

当你没有出现像f x x之类的复杂事物时,你是如何减少表达的。一般来说,有一个函数flip f x y = f y x&#34;翻转参数&#34 ;;您可以随时使用它将f移动到另一侧。如果您包含显式翻转调用,我们会flip (.) map . (.) . filter

答案 2 :(得分:6)

我问了 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,那么基于网络的无点 转换器,例如Blunt(由@TaylorFausak提供),pointfree命令行程序和其他工具。