List 哈斯克尔。跟踪索引以生成新列表
我决定学习Haskell,也学习以更实用的方式思考,所以我试图解决非常简单的练习,试图在这个范例中使用一种好的方法 我试图在Haskell中实现这个简单的练习:List 哈斯克尔。跟踪索引以生成新列表,list,haskell,functional-programming,list-comprehension,List,Haskell,Functional Programming,List Comprehension,我决定学习Haskell,也学习以更实用的方式思考,所以我试图解决非常简单的练习,试图在这个范例中使用一种好的方法 我试图在Haskell中实现这个简单的练习: Input: [2, 4, 1, 1, 2] Output: [True, True, False, False, False, False, True, False, True, True] 因此,输入列表中的元素在 输出列表,奇数元素将为真;每一个 重复次数与输入列表上的值指示的次数相同 如果i ᵗʰ项目是成对的 位置,附加到输出
Input: [2, 4, 1, 1, 2]
Output: [True, True, False, False, False, False, True, False, True, True]
因此,输入
列表中的元素在
输出
列表,奇数元素将为真
;每一个
重复次数与输入
列表上的值指示的次数相同
如果i ᵗʰ项目是成对的
位置,附加到输出True
i次到output
;如果
我 ᵗʰ项目位于奇数位置,追加False
i次
到输出
列表
这似乎是一个非常简单的问题,确实如此。但对我来说,没有任何
函数式编程背景,我不知道如何表达
在哈斯克尔
我试图通过使用λ-函数跟踪当前索引
在列表中
row :: [Integer] -> [Bool]
row xs = [ (last $ zipWith (\i x -> x) [1..] [0..i]) `mod` 2 == 0
| j <- xs, i <- [0..j-1] ]
使用最后一种方法似乎可以:
> let xs = [ 1, 4, 3, 2 ]
> print $ row xs
[True,False,False,False,False,True,True,True,False,False]
但问题并没有解决,因为项目不一定是唯一的:
> let xs = [ 2, 2, 4, 3]
> print $ row xs
[True,True,True,True,True,True,True,True,False,False,False]
因为headfindindices
只给出第一次出现。
(尽管我认为,如果成功的话,这不是一个非常有效的方法
解决这个问题的方法。)
我如何才能以哈斯凯尔式的方式实现我所期待的结果?以下是我将如何做到这一点
row [] = []
row xs = row' xs 0
where row' xs i
| i >= length xs = []
| otherwise = (take (xs !! i) (repeat (isOdd i))) ++ (row' xs (i+1))
isOdd n = n `rem` 2 == 1
但是我没有在这台计算机上安装ghc来测试它。似乎您已经知道可以使用
zip
将元素与其索引配对。对于每个索引i
和相应的元素n
,您希望生成布尔值的n
副本(使用replicate
),该布尔值取决于i
是奇数还是偶数。这意味着map
将每个元组(i,n)
ping到布尔值列表,这样就得到了列表列表([[Bool]]
)。最后一步是将这些列表与concat
合并(可以与map
合并到concatMap
)
或者,如果您不喜欢无点样式:
row xs = concatMap (\(i, n) -> replicate n (odd i)) (zip [1..] xs)
另一个解决方案
row :: [Integer] -> [Bool]
row ns = r' True ns
where
r' :: Bool -> [Integer] -> [Bool]
r' b (n:ns) = replicate b n : r' (not b) ns
r' b [] = []
(未测试)如果输入列表中的数字的索引为偶数,则希望将输入列表中的每个元素转换为元素所说的相等
Bool
s的序列,如果索引为奇数,则希望Bool
为True
因此,您不需要索引,而且最好避免索引,这样可以提供更简单、通常更高效的代码。关键是这个值是交替的,它有一个周期性的模式。为了构建这样的周期模式,前奏曲提供了有用的
cycle :: [a] -> [a]
Prelude> take 10 $ cycle [1,2,3]
[1,2,3,1,2,3,1,2,3,1]
Prelude> take 10 $ cycle [True,False]
[True,False,True,False,True,False,True,False,True,False]
整洁,这正是我们需要的
现在,我们可以将输入列表的每个元素与相应的Bool
配对:
[ 2, 2, 4, 3]
[True,False,True,False,...
我们可以使用zip
生成对,[(2,True),(2,False),…]
,然后使用一个函数将对转换为适当的Bool
s序列
但是这种模式非常普遍,我们有一个特殊的高阶函数,zipWith
因此,如果列表元素的类型是Int
,我们可以编写
row :: [Int] -> [Bool]
row xs = concat $ zipWith replicate xs (cycle [True,False])
对于类型整数
,我们不能使用复制
,但我们可以使用数据中的通用副本
。列表
I interepret
遍历输入列表,如果 ᵗʰ项目处于配对位置,
追加到输出真i次到输出;如果我 ᵗʰ项目处于奇数状态
位置,将False i次附加到输出列表
我认为有两种解释。“对于所有元素,如果第i个元素为偶数,则返回真i次。否则,返回假i次”或“对于所有元素,如果i为偶数,则第i个元素包含(v::Int)应返回真v次,如果i为奇数,则返回假v次”。第二个问题已经有了令人满意的答案,因此我将为第一个问题给出一个答案
有些人喜欢参考指数,但在这种情况下,你不需要担心指数。您可以确定有多少个Bool
s,而无需计算已遍历的元素数
accum f (x:[]) = [x]
accum f (x:xs) = (x):(map f (accum f xs))
此函数接受函数f
和列表。它将f
应用于除第一个元素之外的每个元素,然后对列表的尾部进行递归调用,这将再次将f
应用于剩余的每个元素,以此类推。。。结果是:
accum (+1) [1,1,1,1,1]
[2,3,4,5,6]
功能+1
应用于第二个元素两次,应用于第二个元素三次,以此类推。
你问这对我们有什么帮助?我们可以这样做:
accum (\x -> [head x] ++ x) $ map (\x -> [x]) [2, 4, 1, 1, 2]
[[2],[4,4],[1,1,1],[1,1,1,1],[2,2,2,2,2]]
我们首先将每个元素转换为一个包含单个项的列表。weaccum
和上面看到的lambda,将头部连接到列表。我们现在可以直接转换为Bool
s,无需进一步操作
(map . map) odd $ accum (\x -> [head x] ++ x) $ map (\x -> [x]) [2, 4, 1, 1, 2]
[[False],[False,False],[True,True,True],[True,True,True,True],[False,False,False,False,False]]
您想要的是[Bool]
而不是[[Bool]]
,所以只需concat
。请注意,您可以先使用concat
,然后使用map
而不是map。地图
:
map odd $ concat $ accum (\x -> [head x] ++ x) $ map (\x -> [x]) [2, 4, 1, 1, 2]
我认为您的示例输出应该更改。您在4中缺少了一个false。您没有附加true/false i次,对吗?您将它添加到列表[i]次。似乎工作正常,您只需输入一个
concat
。然而,这是一项不应该用显式累加器递归完成的任务,OP已经走上了一条更好的道路。而且,没有必要定义自己的isOdd
,因为odd
已经在前奏中。解释得很好。我想起来容易多了,你的解释给了我如此多的澄清。当然我不想在哈斯克尔。。。然而,或concat。zipWith(翻转复制)(循环[True,False])
。这几乎是好的。我能看到的唯一问题是:
应该被++
替换。你说得对。我
accum (\x -> [head x] ++ x) $ map (\x -> [x]) [2, 4, 1, 1, 2]
[[2],[4,4],[1,1,1],[1,1,1,1],[2,2,2,2,2]]
(map . map) odd $ accum (\x -> [head x] ++ x) $ map (\x -> [x]) [2, 4, 1, 1, 2]
[[False],[False,False],[True,True,True],[True,True,True,True],[False,False,False,False,False]]
map odd $ concat $ accum (\x -> [head x] ++ x) $ map (\x -> [x]) [2, 4, 1, 1, 2]