Haskell - 合并 foldr 和复制?
Haskell - combine foldr and replicate?
现在我大致了解了如何使用foldr,我通常会在脑海中写一个递归函数,例如当我看到:
sum xs = foldr (+) 0 xs
我认为是:
sum [] = 0
sum (x:xs) = x + sum xs
据我所知,这基本上就是 foldr 的意思。
现在,我有这个代码:
printLine :: [Int] -> String
printLine [] = "+"
printLine (x:xs) = '+' : replicate x '-' ++ printLine xs
它的作用是,它以 [5,4] 为例,然后打印“+-----+----+”。现在,我真的很想知道如何使用 foldr 来写这个。它遵循递归函数的一般思想,所以我认为它应该是可行的。我试过使用 lambda 函数并翻转复制恶作剧,但似乎没有任何效果。有什么建议吗?
试试这个:
withFoldr :: [Int] -> String
withFoldr xs = foldr go "+" xs
where go x b = ('+' : replicate x '-') ++ b
一般来说,当你有一个表达式时
f as = foldr go a bs
那么 a
等于 f []
。 go
函数具有签名 go :: a -> b -> b
对应于 printLine
:
的递归情况
-- match up with go :: a -> b -> b
printLine (x:xs) = '+' : replicate x '-' ++ printLine xs
这里我们用x
标识a
,用printLine xs
标识b
,所以go
变成:
go a b = ('+' : replicate a '-') ++ b
您可以编写一个只需很少改动的版本:
printL = foldr alg "+" where
alg x xs = '+' : replicate x '-' ++ xs
将 xs
视为已经适当处理的组件可能会有所帮助;您处理 x
(通过在 '+'
前面加上 -
复制 x
次),然后将 'already-handled' xs
附加到那个。
另一种可能性:
ghci> let printLine ns = foldr (++) "+" $ map (\n -> '+' : replicate n '-') ns
ghci> printLine [5,2,33]
"+-----+--+---------------------------------+"
我猜您希望将无积分表示作为学术练习传递给 foldr
。以下是我将如何解决这个问题。
首先,我会使用 lambdas 编写表达式:
Prelude> let pp = foldr (\x -> \s -> s ++ (replicate x '-') ++ "+") "+"
Prelude> pp [4,5]
"+-----+----+"
现在,我将努力使表达式点一次释放一个参数。首先,消除s
:
Prelude> let pp = foldr (\x -> flip (++) ((replicate x '-') ++ "+")) "+"
Prelude> pp [4,5]
"+-----+----+"
现在,x
,首先将其移动为内部函数的最后一个参数:
Prelude> let pp = foldr (\x -> flip (++) ((flip replicate '-' x) ++ "+")) "+"
Prelude> pp [4,5]
"+-----+-
然后又一步:
Prelude> let pp = foldr (\x -> flip (++) (flip (++) "+" (flip replicate '-' x))) "+"
Prelude> pp [4,5]
"+-----+----+"
现在有点失控了,所以这是 shorthand 看下一步:
Prelude> let f = flip (++)
Prelude> let g = f "+"
Prelude> let h = flip replicate '-'
Prelude> let pp = foldr (\x -> f (g (h x))) "+"
最后一分很容易!
Prelude> let pp = foldr (f . g . h) "+"
Prelude> pp [4,5]
"+-----+----+"
现在替换 f
、g
和 h
,您将得到答案。然而,这应该只是一个学术练习,我不会想象在实际生产代码中使用这样的东西。
@WillNess 建议使用 operator sections (see right section),我完全忘记了。他们允许一个人在两个地方省略翻转,从而产生更漂亮的东西:
原文:
Prelude> let pp = foldr ((flip (++)) . (flip (++) "+") . (flip replicate '-')) "+"
更改为:
Prelude> let pp = foldr ((flip (++)) . (++ "+") . (`replicate` '-')) "+"
Prelude> pp [4,5]
"+-----+----+"
现在我大致了解了如何使用foldr,我通常会在脑海中写一个递归函数,例如当我看到:
sum xs = foldr (+) 0 xs
我认为是:
sum [] = 0
sum (x:xs) = x + sum xs
据我所知,这基本上就是 foldr 的意思。
现在,我有这个代码:
printLine :: [Int] -> String
printLine [] = "+"
printLine (x:xs) = '+' : replicate x '-' ++ printLine xs
它的作用是,它以 [5,4] 为例,然后打印“+-----+----+”。现在,我真的很想知道如何使用 foldr 来写这个。它遵循递归函数的一般思想,所以我认为它应该是可行的。我试过使用 lambda 函数并翻转复制恶作剧,但似乎没有任何效果。有什么建议吗?
试试这个:
withFoldr :: [Int] -> String
withFoldr xs = foldr go "+" xs
where go x b = ('+' : replicate x '-') ++ b
一般来说,当你有一个表达式时
f as = foldr go a bs
那么 a
等于 f []
。 go
函数具有签名 go :: a -> b -> b
对应于 printLine
:
-- match up with go :: a -> b -> b
printLine (x:xs) = '+' : replicate x '-' ++ printLine xs
这里我们用x
标识a
,用printLine xs
标识b
,所以go
变成:
go a b = ('+' : replicate a '-') ++ b
您可以编写一个只需很少改动的版本:
printL = foldr alg "+" where
alg x xs = '+' : replicate x '-' ++ xs
将 xs
视为已经适当处理的组件可能会有所帮助;您处理 x
(通过在 '+'
前面加上 -
复制 x
次),然后将 'already-handled' xs
附加到那个。
另一种可能性:
ghci> let printLine ns = foldr (++) "+" $ map (\n -> '+' : replicate n '-') ns
ghci> printLine [5,2,33]
"+-----+--+---------------------------------+"
我猜您希望将无积分表示作为学术练习传递给 foldr
。以下是我将如何解决这个问题。
首先,我会使用 lambdas 编写表达式:
Prelude> let pp = foldr (\x -> \s -> s ++ (replicate x '-') ++ "+") "+"
Prelude> pp [4,5]
"+-----+----+"
现在,我将努力使表达式点一次释放一个参数。首先,消除s
:
Prelude> let pp = foldr (\x -> flip (++) ((replicate x '-') ++ "+")) "+"
Prelude> pp [4,5]
"+-----+----+"
现在,x
,首先将其移动为内部函数的最后一个参数:
Prelude> let pp = foldr (\x -> flip (++) ((flip replicate '-' x) ++ "+")) "+"
Prelude> pp [4,5]
"+-----+-
然后又一步:
Prelude> let pp = foldr (\x -> flip (++) (flip (++) "+" (flip replicate '-' x))) "+"
Prelude> pp [4,5]
"+-----+----+"
现在有点失控了,所以这是 shorthand 看下一步:
Prelude> let f = flip (++)
Prelude> let g = f "+"
Prelude> let h = flip replicate '-'
Prelude> let pp = foldr (\x -> f (g (h x))) "+"
最后一分很容易!
Prelude> let pp = foldr (f . g . h) "+"
Prelude> pp [4,5]
"+-----+----+"
现在替换 f
、g
和 h
,您将得到答案。然而,这应该只是一个学术练习,我不会想象在实际生产代码中使用这样的东西。
@WillNess 建议使用 operator sections (see right section),我完全忘记了。他们允许一个人在两个地方省略翻转,从而产生更漂亮的东西:
原文:
Prelude> let pp = foldr ((flip (++)) . (flip (++) "+") . (flip replicate '-')) "+"
更改为:
Prelude> let pp = foldr ((flip (++)) . (++ "+") . (`replicate` '-')) "+"
Prelude> pp [4,5]
"+-----+----+"