【问题标题】:How to use fold to keep the values between function calls如何使用折叠来保持函数调用之间的值
【发布时间】:2013-11-29 23:53:39
【问题描述】:

考虑有一个包含 3 个数字的列表,其中每个数字指定位置的变化,并且它们的值变化如下:

x 从 0 开始,然后增加 1 直到 9,然后 y 做同样的事情,然后 z 做同样​​的事情。当它们全部为 9 时,它们将重置为零。

我已经写了以下内容:

ShiftInX :: ShiftBy->ShiftBy
ShiftInX [z,y,9] = ShiftInY [z,y,0]
ShiftInX [z,y,x] = [z,y,x+1]

ShiftInY :: ShiftBy->ShiftBy
ShiftInY [z,9,0] = ShiftInZ [z,0,0]
ShiftInY [z,y,0] = [z,y+1,0]

ShiftInZ :: ShiftBy->ShiftBy
ShiftInZ [9,0,0] = [0,0,0]
ShiftInZ [z,0,0] = [z+1,0,0]

另外,还有 3 个字符串:

s1 = "ahsgtdfyu"
s2 = "kfhyrdncl"
s3 = "polsedrtd"

给定一个字符串,我想获取给定字符串的每个字符,在 s3 中找到它,将其移动 x,获取该字母并在 s2 中找到它,将其移动 y,获取该字母并在 s3 中找到它并将其移动 z 并重新输入字符。 对给定字符串的每个字符执行相同的操作。在处理每个字符之前,ShiftBy 列表会按照开头所述进行更改。

我编写了一个函数,它获取一个字符和一个 shiftBy 列表作为输入并返回一个字符作为输出。它找到如上所述的输出字符。 findChar :: String -> Char -> [Int] -> Char

问题:我希望能够为给定字符串的每个字符调用此函数,但不会丢失 shiftBy 数字的值,以便每个新字符的 shiftBy 数字都会增加。 processString :: String -> [Int] -> String

我为此使用了 map,但是在处理每个字符后,这些值都会丢失。

我被告知我必须为此使用折叠或显式递归。我了解 fold 的作用,但不知道如何在这里使用它。有人可以帮忙吗?

非常感谢

【问题讨论】:

    标签: haskell


    【解决方案1】:

    你的描述很混乱。如果我理解正确,您有一种加密方案,其中 s1、s2 和 s3 是密钥,而 ShiftBy 列表是一种加密状态,当您遍历要加密的字符串时会发生变化。

    我不明白你所说的“在 s3 中找到它,将它移动 x”是什么意思。你的意思是ROT编码吗?你的意思是找到索引,然后将 x 添加到它以找到下一个字符,围绕结尾旋转?您的意思是实际修改密钥吗?

    无论如何,听起来你当前的字符加密函数的签名是

    encryptChar :: Char -> ShiftBy -> Char
    

    应该是什么时候

    encryptChar :: Char -> ShiftBy -> (Char, ShiftBy)
    

    即它需要返回调整后的加密状态。

    或者,不要传递更改后的加密状态并在外部修改它,因为它不依赖于字符。

    所以关于折叠,关键是让加密状态成为折叠状态的一部分。这是执行此操作的一种方法:

    -- Given:
    encryptChar :: Char -> ShiftBy -> Char
    adjustShiftState :: ShiftBy -> ShiftBy -- really just an alias of ShiftInX
    -- You can fold thus:
    encrypt :: String -> String
    encrypt msg = fst $ foldr foldOne initialState msg
        where initialState = ("", [0, 0, 0])
              foldOne ch (encryptedString, shiftState) =
                (encryptChar ch shiftState : encryptedString, adjustShiftState shiftState)
    

    换句话说,当你折叠时,你总是保留一对到目前为止的加密字符串和当前的移位状态。在加密的最后,您获取结果字符串并丢弃移位状态。

    以上是有效率的;但是,它并不能完全满足您的要求,因为它反向通过字符串。你需要一个左折叠,它(因为: 的工作方式)有点复杂,而且效率可能更低。 (在分析发现问题后,可能可以使用严格的左折叠和严格标记的明智应用。)

    这是左折叠版本:

    encrypt msg = fst $ foldl foldOne msg initialState
        where initialState = ("", [0, 0, 0])
              foldOne (encryptedString, shiftState) ch =
                (encryptedString ++ [encryptChar ch shiftState],
                 adjustShiftState shiftState)
    

    注意使用列表连接而不是简单的 cons。

    【讨论】:

      猜你喜欢
      • 2018-05-09
      • 2017-12-14
      • 1970-01-01
      • 2023-01-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-21
      相关资源
      最近更新 更多