我在Haskell中有以下功能
agreeLen :: (Eq a) => [a] -> [a] -> Int
agreeLen x y = length $ takeWhile (\(a,b) -> a == b) (zip x y)
我正在尝试学习如何编写'惯用'Haskell,它似乎更喜欢使用.
和$
而不是括号,并且在可能的情况下更喜欢无点代码。我似乎无法明确地提到x
和y
。有任何想法吗?
我认为我对点两个参数的任何函数都有同样的问题。
顺便说一下,这只是为了编写好的代码;不是一些“使用任何必要的东西来使它成为免费的”家庭作业。感谢。
(补充评论) 谢谢你的回答。你已经说服我这个功能不会从pointfree中受益。而且你也给了我一些练习转换表达式的好例子。这对我来说仍然很困难,而且它们似乎与Haskell一样重要,因为它指向C语言。
答案 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
作为两个自变量组成f
和g
。可以使用正确的运算符(以及类型签名为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 (==)