我自学Haskell并遇到了map
功能的问题,并正在寻求帮助。我尝试做的是获取列表列表,如果列表为空,那么[]
和其他列表中的一个列表中有一个1
。然后取出1
并将其放入[]
列表。
a = [[2,1,3,1],[],[2,3],[1,2,3],[4]]
并制作
a = [[2,1,3,1],[1],[2,3],[2,3],[4]]
到目前为止,我有一些东西,我没有完整的东西。
((map (\k-> if ([])&&(elem 1 heads))a)
where heads = map head a
但我也必须这样做才能a = [[1,2,1,3,1],[],[2,3],[1,2,3],[4]]
将给a = [[2,1,3,1],[1],[2,3],[1,2,3],[4]]
,因此它只会将一个1
移到空列表中。
我知道这不行,但我现在已经尝试了几个小时,但我没有到达任何地方
答案 0 :(得分:3)
在这里,我试图为a编写易于理解的功能解决方案 初学者。当然,这可以更有效地实施。
doStuff :: Integral a => [[a]] -> [[a]]
doStuff xs = let n = min holes donors in go n n xs
where go _ _ [] = []
go 0 m ([]:ys) = [] : go 0 m ys
go n m ([]:ys) = [1] : go (n - 1) m ys
go n 0 ((1:zs):ys) = (1:zs) : go n 0 ys
go n m ((1:zs):ys) = zs : go n (m - 1) ys
go n m (y:ys) = y : go n m ys
holes = count null xs
donors = count goodish xs
count f = length . filter f
goodish (1:_) = True
goodish _ = False
它甚至有效:
λ> doStuff [[2,1,3,1],[],[2,3],[1,2,3],[4]]
[[2,1,3,1],[1],[2,3],[2,3],[4]]
λ> doStuff [[1,2,1,3,1],[],[2,3],[1,2,3],[4]]
[[2,1,3,1],[1],[2,3],[1,2,3],[4]]
λ> doStuff [[1,2,1,3,1],[],[2,3],[1,2,3],[4],[],[]]
[[2,1,3,1],[1],[2,3],[2,3],[4],[1],[]]
答案 1 :(得分:1)
这看起来像是一个以增量方式解决的可怕挑剔问题。因此,我建议你考虑单片解决方案。首先计算空列表的总数以及以1
开头的列表总数。有了这些结果,再次攻击原始输入。如果您熟悉,foldl'
可以轻松完成计数,或者您可以使用显式递归,或者如果您不关心效率,则可以过滤和计数两次;使用显式递归最终攻击最简单。
答案 2 :(得分:1)
如果从正确的方向接近,问题就很简单了。作为第一个近似值,您可以将函数定义为输入列表上的左侧折叠:
fun :: Eq a => a -> [[a]] -> [[a]]
fun z xss = newList
where (foundZ, newList, _) = foldl ... (False, [], False) xss
请注意,fold将返回两个值,即结果列表和一个布尔值,表示列表中是否找到1(或者在本例中为参数z
)。第三个参数表示列表是否已被替换(我们只想进行一次替换)。如果我们聪明(我们将会),我们可以在传递给foundZ
的函数体内使用foldl
的值 - 也就是说,我们可以在它显然被计算之前使用它。剩下的就是将参数函数写入foldl:
\(fn,xss',rpl) xs ->
case xs of
...
考虑xs
的不同可能性:它是空列表,它是包含z
作为其头部的列表,以及每个其他列表:
[] | not rpl -> ...
(x:r) | x == z && not fn -> ...
_ -> (fn, xs:xss',rpl)
请注意,当我们检查x == z
时,我们也会检查not fn
,因为我们只想从单个列表中选择一个z
,所以如果我们已找到一个,我们就不会想再做一次。对于空列表,我们出于同样的原因检查not rpl
。最后一种情况很明显,但前两种可能不是。
[] | not rpl -> (fn, (if foundZ then [z] else []):xss', True)
空列表的情况简单检查是否找到z
- 如果是,则返回包含z
的单个列表。
(x:r) | x == z && not fn -> (True , r:xss', rpl)
以z
开头的列表的大小写为True
返回found
,并将该列表的 tail 添加到结果中(这是如何1被删除)。
把它们放在一起,你得到一个简单的功能:
fun :: Eq a => a -> [[a]] -> [[a]]
fun z xss = reverse newList
where (foundZ, newList, _) =
foldl
(\(fn,xss',rpl) xs ->
case xs of
[] | not rpl -> (fn, (if foundZ then [z] else []):xss', True)
(x:r) | x == z && not fn -> (True , r:xss', rpl)
_ -> (fn, xs:xss', rpl)
) (False, [], False) xss
和
>fun 1 [[2,1,3,1],[],[2,3],[1,2,3],[4]]
[[2,1,3,1],[1],[2,3],[2,3],[4]]
>fun 1 [[1,2,1,3,1],[],[2,3],[1,2,3],[4]]
[[2,1,3,1],[1],[2,3],[1,2,3],[4]]
答案 3 :(得分:0)
IMO map
对于这项工作来说并不是真正的功能。从概念上讲,map
使用仅查看元素本身的函数来转换集合的每个元素。显然你可以编写一个map
函数使用lambdas,范围等函数使用“外部”变量,但我建议尽可能接近map
的概念。
“把那个1放在[]列表中的想法。”暗示(采取和放置)带有副作用的命令式方法,我们希望尽可能避免使用像haskell这样的纯函数式语言。
你可以用另一种方式来考虑它:没有必要实际“移动”1
,即知道哪个1
最终在哪个[]
。计算空列表的数量(让我们称之为x
)以及以1
(y
)开头的列表数量就足够了,我们只需创建一个新列表< / p>
min(x,y)
[1]
次出现
tail
min(x,y)
列表中的1
可能的改进实施:
redistributeOnes :: [[Int]] -> [[Int]]
redistributeOnes xs = redistributeOnes' n n xs where
n = min (length $ filter (==[]) xs) (length $ filter (\l -> if l == [] then False else (head l) == 1) xs)
redistributeOnes' _ _ [] = []
redistributeOnes' 0 0 xs = xs
redistributeOnes' n m ([]:xs) | n > 0 = [1] : redistributeOnes' (n-1) m xs
| otherwise = [] : redistributeOnes' n m xs
redistributeOnes' n m (x:xs) | head x == 1 && m > 0 = (tail x) : redistributeOnes' n (m-1) xs
| otherwise = x : redistributeOnes' n m xs
试验:
λ> redistributeOnes [[], [1,2]]
[[1],[2]]
λ> redistributeOnes [[], [1,2], [], [], [1,2,3]]
[[1],[2],[1],[],[2,3]]
λ> redistributeOnes [[], [1,2], [], [1,2,3]]
[[1],[2],[1],[2,3]]
λ> redistributeOnes [[]]
[[]]
λ> redistributeOnes [[], [1,2,3]]
[[1],[2,3]]
λ> redistributeOnes [[1,2,3]]
[[1,2,3]]
λ> redistributeOnes [[1,2,3], [], [], [], [2,5,1], [1,2], [], [1,100]]
[[2,3],[1],[1],[1],[2,5,1],[2],[],[100]]