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]
"+-----+----+"

现在替换 fgh,您将得到答案。然而,这应该只是一个学术练习,我不会想象在实际生产代码中使用这样的东西。


@WillNess 建议使用 operator sections (see right section),我完全忘记了。他们允许一个人在两个地方省略翻转,从而产生更漂亮的东西:

原文:

Prelude> let pp = foldr ((flip (++)) . (flip (++) "+") . (flip replicate '-')) "+"

更改为:

Prelude> let pp = foldr ((flip (++)) . (++ "+") . (`replicate` '-')) "+"
Prelude> pp [4,5]
"+-----+----+"