为了尝试在Haskell中编写Havel-Hakimi算法,我编写了以下函数从列表的前n个元素中减去1。
decrease_first :: Int -> [Int] -> [Int]
decrease_first 0 l = l
decrease_first n (x:xs) = (x-1):decrease_first (n-1) xs
这段代码有效,但是当我尝试为Haskell测试做准备时,我确信这段代码相当蹩脚,我想重写这个函数如下:
decrease_first :: Int -> [Int] -> [Int]
decrease_first = map (subtract1) . take
我知道这不是完全一样的事实,但我只是想更好地理解部分应用的功能等,并且想要尝试这个功能。事实证明这不是最好的想法,因为我没有得到这个编译(类型定义),我留下了一个巨大的思想 - 你知道什么,因为下一个代码确实有效,即使我认为它当量:
decrease_first n = map (subtract1) . take n
并且此代码再次失败:
decrease_first n l = map (subtract1) . take n l
此外,我尝试寻找一种将函数应用于列表的第一个元素的好方法,但我找不到任何东西。第一种方式可能是最有效的方法,但我想知道是否有某种方法可以在列表的第一个元素上应用任何函数。我的想法最终是做了类似的事情:
decrease_first n l = map (subtract 1) (take n l) ++ drop n l
它的工作方式与此类似,但它看起来并不像我想象的那么好。所以,如果有人能帮助我解决我的类型问题,我将非常感激。
提前致谢
答案 0 :(得分:3)
您的第一次免费尝试不起作用,因为.
使用一个参数编写函数,而take
使用两个。真正发生的是像
decrease_first = map (subtract1) . take
相当于
decrease_first n = map (subtract1) (take n)
但现在take n
不是列表,因此会出现类型错误。
相反,在
中decrease_first n l = map (subtract1) . take n l
我们认为take n l
是一个列表,而不是一个函数。在这里你需要应用,而不是功能组合,例如
decrease_first n l = map (subtract1) $ take n l
你的最后一次尝试对我来说很好看:
decrease_first n l = map (subtract 1) (take n l) ++ drop n l
作为变体,您可以使用splitAt
将take
和drop
放在一起:
import Data.List
decrease_first n l = map (subtract 1) left ++ right
where (left,right) = splitAt n l
或者:
decrease_first n l =
(\(left, right) -> map (subtract 1) left ++ right) (splitAt n l)
这并不比上面的好。