我正在尝试将此伪代码转换为Haskell:
function whatever(list)
int somestate
foreach list as item
if item === 1
state++
else
state--
endforeach
return state
现在这样的事情显然是错误的(状态是3):
test :: [Int] -> Int
test item
| head item > 1 = 1
| otherwise = newItem
where newItem = tail item
在Haskell中实现这样的目标的最佳方法是什么?
答案 0 :(得分:4)
您只需使用该函数的另一个参数即可完成此操作:
test [] counter = counter
test (x:xs) counter
| x == 1 = test xs (counter + 1)
| otherwise = test xs (counter - 1)
这里我们使用模式匹配(x:xs)
将列表的头部绑定到x,将列表的尾部绑定到xs。一旦我们到达空列表,我们就会返回最终的计数器。
我们必须使用初始计数器调用此函数,如此
test [1..3] 0
然而,我们可以通过不必传递初始计数器,通过累积计数器来做得更好,就像这样。
test [] = 0
test (x:xs)
| x == 1 = test xs + 1
| otherwise = test xs - 1
这次,每次该值等于1时,我们在空列表中的函数结果中加一。最终我们将满足基本情况,即0,其余的值将加起来给我们最终值。
然而,这种在列表上递归并执行值非常常见的模式。我们可以使用折叠:
test = foldl' (\acc x -> if x == 1 then acc + 1 else acc - 1) 0
这基本上是我们以前的递归函数。
有一些方法可以在Haskell中存储持久状态,但通常我们不需要像这样的简单情况。
答案 1 :(得分:4)
我想我可能会这样做:
whatever xs = length ones - length others where
(ones, others) = partition (1==) xs
唯一可以让我停下来的是对长名单的考虑;如果重要的是列表不能同时存在于内存中,那么这将无法正常工作。
答案 2 :(得分:1)
另一种选择:
> sum . map (bool (-1) 1 . (==1)) $ [1,2,2]
-1
> sum . map (bool (-1) 1 . (==1)) $ [1,2,2,1]
0
> sum . map (bool (-1) 1 . (==1)) $ [1,2,2,1,1,1,1,1]
4
请注意,bool (-1) 1 . (==1)
也可以使用基本语法编写
(\x -> if x==1 then 1 else -1)
答案 3 :(得分:1)
你只需调整它并随便洗几下,就像
一样foo (list) =
-- int somestate
-- foreach list as item
-- foreach item in list
do item <- list
if item == 1
then return incr -- state
else return decr -- state
-- return state
incr state = state + 1
decr state = state - 1
并且已经为自己创建了一个的东西,并推导出类型!
foo :: (Num a, Num b, Monad m, Eq a) => m a -> m (b -> b)
我们知道m ~ []
所以它实际上是
foo :: (Num a1, Num a, Eq a) => [a] -> [b -> b]
那么,我们可以在一行中对一堆状态更新函数(如incr
/ decr
)做些什么呢?有几件事:
import Control.Monad
import Data.Monoid
import Data.Foldable
sequence . foo :: (Num a, Num b, Eq a) => [a] -> b -> [b]
foldr (.) id . foo :: (Num a, Num b, Eq a) => [a] -> b -> b
appEndo . foldMap Endo . foo
:: (Num a, Num b, Eq a) => [a] -> b -> b
foldl (.) id . foo :: (Num a, Num b, Eq a) => [a] -> b -> b
appEndo . getDual . foldMap (Dual . Endo) . foo
:: (Num a, Num b, Eq a) => [a] -> b -> b
当然不是第一个。
foldl
似乎是这里的门票。但话又说回来,它违背了非常规的乘数用户需要。真的,它是
foo :: (Num t, Num a, Eq a) => [a] -> t -> t
foo = foldr g id
where
g x r state | x==1 = r (incr state) -- or even `r $! incr state`
| otherwise = r (decr state) -- `r $! decr state`
或更简单,
foo :: (Num t, Num a, Eq a) => [a] -> t -> t
foo = foldr g id
where
g x r | x==1 = r . incr
| otherwise = r . decr
甚至只是
foo :: (Num t, Num a, Eq a) => [a] -> t -> t
foo = foldr (\x -> if x==1 then (. incr) else (. decr)) id
实际上是我们一直拥有Dual
的那个。我认为。有乐趣验证这个(或其他)。 :)
更新:将foldr (.) id . foo
融合到一个功能中会带来更好看的效果
foo :: (Num t, Num a, Eq a) => [a] -> t -> t
foo = flip $ foldr (\x -> if x==1 then incr else decr)
但它以相反的顺序处理状态。