微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何在功能上表达依赖列表中相邻元素的过滤器

有几次我想遍历一个列表并挑选出具有某些属性的元素,这些属性也依赖于列表中的下一个元素.举一个简单的例子,我有一些代码可以计算函数f在指定的区间[a,b]上改变符号的次数.这在C等命令式语言中相当明显:

for(double x=a; x<=b; x+=(b-a)/n){
    s*f(x)>0 ? : printf("%e %e\n",x,f(x)),s=sgn(f(x));
    }

在Haskell中,我的第一直觉是用它的尾部压缩列表,然后应用过滤器并用fst或其他任何东西提取元素.但这似乎笨拙而且效率低下,所以我把它变成了折叠:

signChanges f a b n = tail $      
    foldl (\(x:xs) y -> if (f x*f y)<0 then y:x:xs else x:xs) [a] [a,a+(b-a)/n..b]

无论哪种方式,我觉得有一种“正确”的方式来做到这一点(因为在Haskell经常出现)并且我不知道(或者只是没有意识到)它是什么.任何有关如何以更惯用或更优雅的方式表达这一点的帮助将不胜感激,一般来说,建议如何找到“正确”的做事方式.

解决方法

这是一个使用paramorphism的“版本”(与问题不完全相同 – 但它应该足以说明一个paramorphism),首先我们需要para,因为它不在标准库中:

-- paramorphism (generalizes fold)
para :: (a -> ([a],b) -> b) -> b -> [a] -> b
para phi b = step
  where step []     = b
        step (x:xs) = phi x (xs,step xs)

使用paramorphism就像使用折叠一样,但是看到累加器我们可以看到其余的输入:

countSignChanges :: [Int] -> Int
countSignChanges = para phi 0
  where
    phi x ((y:_),st)  = if signum x /= signum y then st+1 else st
    phi x ([],st)  = st


demo = countSignChanges [1,2,-3,4,-5,-6]

与拉链相比,para的优点在于我们可以根据需要偷看其余的输入.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐