在Haskell中使用fold*增加列表

在Haskell中使用fold*增加列表,haskell,fold,Haskell,Fold,我试图在Haskell中解决以下问题:给定一个整数,返回其数字列表。约束条件是我必须只使用一个fold*函数(*={r,l,1,l1}) 没有这样的约束,代码很简单: list_digits :: Int -> [Int] list_digits 0 = [] list_digits n = list_digits r ++ [n-10*r] where r = div n 10 但是我如何使用fold*从一个空

我试图在Haskell中解决以下问题:给定一个整数,返回其数字列表。约束条件是我必须只使用一个fold*函数(*={r,l,1,l1})

没有这样的约束,代码很简单:

list_digits :: Int -> [Int]
list_digits 0 = []
list_digits n = list_digits r ++ [n-10*r] 
                where
                     r = div n 10
但是我如何使用fold*从一个空列表中增长一个数字列表呢


提前谢谢

这是家庭作业吗?作业要求您使用
foldr
,这很奇怪,因为这是
展开器
的自然用法,而不是
foldr
<代码>展开器::(b->Maybe(a,b))->b->[a]构建一个列表,而
foldr::(a->b->b)-->b->[a]->b
使用一个列表。如果使用
foldr
实现此函数,将会出现可怕的扭曲

listDigits :: Int -> [Int]
listDigits = unfoldr digRem
    where digRem x
              | x <= 0 = Nothing
              | otherwise = Just (x `mod` 10, x `div` 10)
但是
unfover
允许我们在更高的层次上表达循环<代码>展开器捕获“一次构建一个列表一个项目”的模式,并将其显式化。您不必仔细考虑循环的顺序行为,并意识到列表一次只构建一个元素,就像您在Python代码中所做的那样;您只需知道展开器的功能。当然,使用折叠和展开进行编程需要一点时间来适应,但它的表现力更高,这是值得的

如果你的作业是用机器标记的,并且确实需要你在程序文本中键入单词
foldr
(你应该问你的老师他们为什么这么做,并且)你可以使用以下“
id[]
-as-
foldr
”功能玩一个鬼祟的把戏:

obfuscatedId = foldr (:) []
listDigits = obfuscatedId . unfoldr digRem

虽然作业的意思可能是
unfolder
,但如果您将
foldr
用作hylomorphism,也就是说,在创建一个列表的同时撕掉另一个列表,则可以使用
foldr
编写此作业

digits :: Int -> [Int]
digits n = snd $ foldr go (n, []) places where
  places = replicate num_digits ()
  num_digits | n > 0     = 1 + floor (logBase 10 $ fromIntegral n)
             | otherwise = 0
  go () (n, ds) = let (q,r) = n `quotRem` 10 in (q, r : ds)
实际上,我们在这里使用的是
foldr
作为“状态映射”。我们提前知道 我们需要输出多少个数字(使用log10),而不是这些数字是什么,所以我们使用 单位(
()
)值作为这些数字的替代

如果你的老师是一个固执的人,只因为在顶级课程上有一个
foldr
,你可以 取消使
go
partial:

digits' :: Int -> [Int]
digits' n = foldr go [n] places where
  places = replicate num_digits ()
  num_digits | n > 0     = floor (logBase 10 $ fromIntegral n)
             | otherwise = 0
  go () (n:ds) = let (q,r) = n `quotRem` 10 in (q:r:ds)
这在非正数上的行为略有不同:

>>> digits 1234567890
[1,2,3,4,5,6,7,8,9,0]
>>> digits' 1234567890
[1,2,3,4,5,6,7,8,9,0]
>>> digits 0
[]
>>> digits' 0
[0]
>>> digits (negate 1234567890)
[]
>>> digits' (negate 1234567890)
[-1234567890]

您确定不能使用
展开器
?还是别的什么?所有的折叠功能都需要一些列表(或
可折叠的
)作为折叠的输入…Alec,我不熟悉unfold*。我认为,这可能就是解决问题的方法(以及问题需求的实际含义)。谢谢。使用
list_digits=unfolder(\i->如果i==0,那么就不需要其他任何东西了,让(q,r)=quotRem i 10在(r,q)中),你可以很容易地将数字倒过来。
>>> digits 1234567890
[1,2,3,4,5,6,7,8,9,0]
>>> digits' 1234567890
[1,2,3,4,5,6,7,8,9,0]
>>> digits 0
[]
>>> digits' 0
[0]
>>> digits (negate 1234567890)
[]
>>> digits' (negate 1234567890)
[-1234567890]