我在Haskell中编写了一个代码,该代码采用0和1的列表,例如[1,1,0,0,1,0,1]返回列表(如( 3,4)。
这是我的代码:
inc :: Int -> Int
inc x = (\x -> x + 1) x
count :: [Int] -> (Int,Int)
c = (0,0)
count x =
if null x
then c
else if head x == 0
then do
inc (fst c)
count (tail x)
else if head x == 1
then do
inc (snd c)
count (tail x)
我也尝试过以受保护的形式进行操作:
count :: [Int] -> (Int,Int)
c = (0,0)
count x
| null x = c
| head x == 0 = inc (fst c) >> count (tail x)
| head x == 1 = inc (snd c) >> count (tail x)
主要问题是我不确定如何在一个then语句中实现两个功能。
您必须全神贯注。do { inc (fst c); count (tail x) }
只有c
某种可变状态变量,类似的东西才有意义。哈斯克尔变量是不可变的,所以inc
不能修改的fst
的c
,它只能给你一个修改后的副本。如果您重写inc
为完全等效的更简单形式,则可能会变得更加清晰:
inc x = x + 1
(实际上inc = (+1)
也可以。)
现在,在中count
,您尝试通过递归循环继续进行并递增单个累加器变量。您可以这样做,但是您需要明确地将修改后的版本传递给递归调用:
count = go (0,0)
where go :: (Int,Int) -> [Int] -> (Int,Int)
go c x
| null x = c
| head x == 0 = go (first inc c) (tail x)
| head x == 1 = go (second inc c) (tail x)
在Haskell中,这种定义小的本地助手函数(go
只是一个任意名称,我也可以称呼它getTheCountingDone
)并将其用作递归的“循环体”的模式非常普遍。基本上go (0,0)
“初始化”c
到该值(0,0)
,然后开始第一个循环迭代。对于第二次迭代,您递归到例如go (first inc c)
,即,使用更新后的c
变量再次开始循环。
我已经使用first
并second
用于增加各自的元组字段。fst
仅读取第一个字段,即为您提供其值,而first
从element-update函数创建一个tuple-update函数。代替import Control.Arrow
您也可以自己定义此:
first :: (a->b) -> (a,y) -> (b,y)
first f (a, y) = (f a, y)
second :: (a->b) -> (x,a) -> (x,b)
second f (x, a) = (x, f a)
(该Control.Arrow
版本实际上更通用,但是您不必担心–您可以以相同的方式使用它。)
请注意,在Haskell中,使用head
和进行列表解构tail
非常困难:很容易出错-您可能会忘记在访问元素之前检查列表是否为空,这会引发讨厌的运行时错误。更好地使用模式匹配:
count = go (0,0)
where go c [] = c
go c (0:xs) = go (first inc c) xs
go c (1:xs) = go (second inc c) xs
实际上,这仍然是不安全的:您没有详尽的案例。如果列表中包含非零或一的内容,则该函数将失败。也许您想计算所有零和非零元素?
count = go (0,0)
where go c [] = c
go c (0:xs) = go (first inc c) xs
go c (_:xs) = go (second inc c) xs
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句