我正在尝试将以下haskell代码转换为指向自由样式,但没有用。
bar f g xs = filter f (map g xs )
我是haskell的新手,任何帮助都会很棒
答案 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。我将使用小写字母表示文字变量,大写字母表示更复杂的表达式。
\x -> x
,请替换为id
\x -> A
,其中A
是x
未出现const A
的任何表达式,请替换为\x -> A x
x
A
,A
中未\x -> A B
,则替换为x
。这被称为&#34; eta contraction&#34;。A
,那么
B
和(\x -> A) <*> (\x -> B)
同时发生x
,请替换为A
。flip (\x -> A) B
发生x
,请替换为B
A . (\x -> B)
发生x
,请替换为A
,B
或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
中没有出现{{1}},那么我们应该已经使用了另一条规则。然后向内工作,消除你创造的lambda。让我们使用这个例子:
{{1}}
你去了,现在试试吧!决定使用哪条规则并不是很聪明:使用适用的任何规则,您将取得进展。
答案 1 :(得分:7)
现有的两个答案都没有以一种明确的方式回答你的具体问题:一个是&#34;这是规则,为自己解决问题&#34;另一个是&#34;这里是答案,没有关于规则如何生成它的信息。&#34;
前三个步骤非常简单,包括通过编写x
从h 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
命令行程序和其他工具。