如何将两个参数的Haskell函数重写为无点样式

时间:2012-11-17 00:33:10

标签: haskell pointfree

我在Haskell中有以下功能

agreeLen :: (Eq a) => [a] -> [a] -> Int
agreeLen x y = length $ takeWhile (\(a,b) -> a == b)  (zip x y)

我正在尝试学习如何编写'惯用'Haskell,它似乎更喜欢使用.$而不是括号,并且在可能的情况下更喜欢无点代码。我似乎无法明确地提到xy。有任何想法吗?

我认为我对点两个参数的任何函数都有同样的问题。

顺便说一下,这只是为了编写好的代码;不是一些“使用任何必要的东西来使它成为免费的”家庭作业。

感谢。


(补充评论) 谢谢你的回答。你已经说服我这个功能不会从pointfree中受益。而且你也给了我一些练习转换表达式的好例子。这对我来说仍然很困难,而且它们似乎与Haskell一样重要,因为它指向C语言。

4 个答案:

答案 0 :(得分:45)

  

并且在可能的情况下也更喜欢无点代码。

不“尽可能”,但“它提高了可读性(或具有其他明显的优势)”。

点免费

agreeLen x y = length $ takeWhile (\(a,b) -> a == b)  (zip x y)

第一步是将($)向右移动,并用(.)替换你拥有的那个:

agreeLen x y = length . takeWhile (\(a,b) -> a == b) $ zip x y

现在,您可以进一步向右移动:

agreeLen x y = length . takeWhile (uncurry (==)) . zip x $ y

在那里你可以立即砍掉一个论点,

agreeLen x = length . takeWhile (uncurry (==)) . zip x

然后,您可以将其重写为合成运算符的前缀应用程序

agreeLen x = (.) (length . takeWhile (uncurry (==))) (zip x)

你可以写

f(g x)

作为

f . g $ x

一般来说,这里有

f = (.) (length . takeWhile (uncurry (==)))

g = zip,给予

agreeLen x = ((.) (length . takeWhile (uncurry (==)))) . zip $ x

可以轻松删除参数x。然后,您可以将(.)的前缀应用程序转换为一个部分并获取

agreeLen = ((length . takeWhile (uncurry (==))) .) . zip

但是,这比原版的可读性差,所以除了练习将表达式转换为无点样式之外,我不建议这样做。

答案 1 :(得分:12)

你也可以使用:

agreeLen :: (Eq a) => [a] -> [a] -> Int
agreeLen x y = length $ takeWhile id $ zipWith (==) x y

惯用语Haskell是更容易阅读的东西,不一定是最无点的东西。

答案 2 :(得分:1)

正如丹尼尔(Daniel)的出色答案所指出的那样,您的问题是将f作为一个自变量而将g作为两个自变量组成fg。可以使用正确的运算符(以及类型签名为f ??? g来写(c -> d) -> (a -> b -> c) -> a -> b -> d。 这对应于(.).(.)运算符(请参阅there),该运算符有时定义为.:。在这种情况下,您的表情就会变成

length . takeWhile (uncurry (==)) .: zip

如果您习惯使用.:运算符,那么此无磅版本非常容易阅读。我也可以改用(<$$$>) = fmap fmap fmap并获取

length . takeWhile (uncurry (==)) <$$$> zip

答案 3 :(得分:0)

另一种简洁,无点的解决方案:

agreeLen = ((length . takeWhile id) .) . zipWith (==)

等效地:

agreeLen = (.) (length . takeWhile id) . zipWith (==)