使用takewhile和iterate的Idiomatic haskell

时间:2017-06-23 14:39:06

标签: haskell

我试图通过the CIS 194 course at the University of Pennsylvania课程在线学习Haskell。在其中一个练习中,学生应该重新编写一个函数:

fun1 :: [Integer] -> Integer
fun1 [] = 1
fun1 (x:xs)
  | even x = (x - 2) * fun1 xs
  | otherwise = fun1 xs

进入一个更加惯用的'哈斯克尔。我重写了这个函数

fun1 :: [Integer] -> Integer
fun1 xs =
  let spl = partition even xs
  in foldl (*) 1 ((map (subtract 2) (fst spl)) ++ snd spl)

似乎...... lispy。它将角色的几乎字符翻译成我在clojure中编写它的方式。在赋值中,它提供了使用Prelude中的takeWhileiterate来重写函数的提示。虽然我从表面上理解这些函数的作用,但对我来说,如何重写该函数以使用它们并不是很明显。如何使用takeWhileiterate

重写

1 个答案:

答案 0 :(得分:3)

这实际上与takeWhile没什么关系。您只需针对给定列表中的每个偶数x-2计算x的产品。事实上:

fun1 :: [Integer] -> Integer
fun1 [] = 1 -- the product of an empty list is 1
fun1 (x:xs)
  -- if the number is even, we multiply (x-2) with the remaining part
  | even x = (x - 2) * fun1 xs
  -- if the number is odd, we ignore x and return the fun1 of the tail
  | otherwise = fun1 xs

所以我们可以把它写成:

fun1 :: Integral i => [i] -> i
fun1 = product . map (subtract 2) . filter even

如果您无法使用product,您可以像使用问题一样使用:

fun1 :: Integral i => [i] -> i
fun1 = foldl (*) 1 . map (subtract 2) . filter even

这就是我们所说的 pointfree 版本:fun1或任何lambda表达式的头部都没有参数。因此,我们不考虑价值观,而是考虑功能。

对于列表[1,3,4],这会生成:

Prelude> (foldl (*) 1 . map (subtract 2) . filter even) [1,3,4]
2