无点风格的简单Haskell函数
我试图理解如何在Haskell中将函数转换为无点表示法。我看到了,但它比我要找的更复杂。我觉得我理解它背后的逻辑,但当我试图在代码中执行一些简单的示例时,我会遇到编译错误。我想尝试以无点样式编写此函数:无点风格的简单Haskell函数,haskell,pointfree,Haskell,Pointfree,我试图理解如何在Haskell中将函数转换为无点表示法。我看到了,但它比我要找的更复杂。我觉得我理解它背后的逻辑,但当我试图在代码中执行一些简单的示例时,我会遇到编译错误。我想尝试以无点样式编写此函数: fx=5+8/x我将其重新排列为fx=(+)5$(/)8x 所以,我想可能是这样的: f = (+) 5 $ (/) 8 但当我在ghci中运行此命令时,我得到以下消息: 没有(Num(a0->a0))的实例 源自测试时的文字“5”。hs:3:9 可能的修复方法:为(Num(a0->a0))添
fx=5+8/x
我将其重新排列为fx=(+)5$(/)8x
所以,我想可能是这样的:
f = (+) 5 $ (/) 8
但当我在ghci中运行此命令时,我得到以下消息:
没有(Num(a0->a0))的实例
源自测试时的文字“5”。hs:3:9
可能的修复方法:为(Num(a0->a0))添加实例声明
在“(+)”的第一个参数中,即“5”
在“($)”的第一个参数中,即“(+)5”
在表达式中:(+)5$(/)8
失败,已加载模块:无。
我不明白“没有…的实例”的信息。我需要做什么才能以无点样式编写此函数?
$
的优先级非常低。所以,fx=(+)5$(/)8x
实际上意味着fx=(+)5$(/)8x
。相反,将其改写为
f x = (+) 5 ( (/) 8 x)
f x = ((+) 5) ( ((/) 8) x)
f x = ((+) 5) . ( ((/) 8) ) x
f = ((+) 5) . ( (/) 8 )
f = (5+) . (8/)
最后一个表达式是有意义的:f是两个运算的组合,首先将8除以1,然后将5加到结果中。请记住,g.h
的意思是“应用h,然后应用g以获得结果”。可以使用cabal install pointfree
安装“无点”程序,并向您展示如何以无点样式编写表达式。例如:
$ pointfree "f x = 5 + 8/x"
f = (5 +) . (8 /)
对这种转换的解释:
(+a)=\b->b+a
函数获取第二个参数的结果,这是一个单参数函数,并将其应用于第一个参数你真的很接近。请允许我再添加一个
$
来说明:
f x = (+) 5 $ (/) 8 $ x
应该清楚的是,表达式(+)5
是一个接受一个数字输入并产生一个数字输出的函数。表达式(/)8
也是如此。因此,无论输入什么数字,x
,首先应用(/)8
“函数”,然后应用(+)5
“函数”
只要有一个由$
分隔的函数链,就可以用
替换除最右边以外的所有函数。这意味着,如果有a$b$c$d
,这相当于a。B加元d
f x = (+) 5 . (/) 8 $ x
现在,让我们实际删除$
并用括号代替
f x = ((+) 5 . (/) 8) x
现在应该很清楚,您可以从两侧移除尾部的x
:
f = (+) 5 . (/) 8
这是主要的想法。如果您有f x=expr x
,您可以将其“eta减少”为f=expr
。为了生成无点代码,您只需识别较大的函数是如何由较小的函数组成的。无点代码有时需要部分应用(在这种情况下,部分应用了(+)5
和(/)8
)。当你不想去想它的时候,“无点”程序是非常有帮助的;haskell irc频道上的Lambdabot将此程序用作插件,因此您甚至不必自己安装;只要问问:
<DanBurton> @pl let f x = 5 + 8 / x in f
<lambdabot> (5 +) . (8 /)
@pl让f x=5+8/x在f中
(5 +) . (8 /)
从lambda演算(Haskell是其变体)项到SKI项(完全无点函数,仅使用const
(K)、id
(I)和
(S)的转换可以通过以下简单规则完成:
\x->x
转换为id
李>
\x->y
中不出现x
的y
转换为const y
李>
\x->f g
转换为f'g'
其中
是f'
和\x->f
是g'
的翻译\x->g
从何而来。最后一次翻译有一个特例:如果f
没有任何自由出现的x
,那么\x->fg
将转换为常量f(\x->g)
,它等于f。(\x->g)
使用这些规则,我们可以转换您的函数:
f = \x -> ((+) 5) (((/) 8) x) = -- by the special-case (.) rule
((+) 5) . (\x -> (((/) 8) x)) = -- by eta-reduction ((\x -> f x) = f)
((+) 5) . ((/) 8)
Eta降低不是完成翻译的必要条件,但如果没有它,我们会变得更混乱。例如,最后一步将产生
(++)5)。((/) 8) . id
取而代之。我想你可能会对这个问题感到困惑。我个人强迫自己使用无点风格,因为:1。我被迫使我的函数更简单,从而使它们更可重用2。实际上,我很可能会费心重用已经编写好的函数,从而减少代码重复。请注意,(.)是关联的,而($)不是,因此(.)在重构方面提供了更大的灵活性。要使用(.)你需要掌握无点风格。此外,一元组合符,如liftM2、fmap、>>=和>=>几乎迫使您学习无点风格。@JFritsch-point-free并不总是编写函数的最清晰方式。然而,在适当的时候,它可以帮助你对函数进行推理,因为无点函数只是其他函数的组合。这实际上很好:所有三个投票结果都显示了问题的不同角度。