【问题标题】:Haskell - string functionHaskell - 字符串函数
【发布时间】:2014-02-16 17:49:45
【问题描述】:

我需要写一个函数,它在字符串中寻找“Z”,当这个函数在第i个索引上找到它时,它会将i+3个字符附加到表中。

这是我的代码:

someFun :: String => String -> String -> String
someFun "" (r:rs) = (r:rs)
someFun (a:b:c:d:xs) (r:rs)
    | a == "Z" = someFun xs ((r:rs)++d)
    | otherwise = someFun (b:c:d:xs) (r:rs)

由于我在 Haskell 编程方面的经验不佳,我遇到了一堆我不知道如何解决的错误。

编辑: 如果输入是“(C(N(Z'p'))(A(K(Z'p')(Z'q'))(Z'r')))” 它的输出应该是:['p','q','r']

【问题讨论】:

  • 什么是“桌子”?显示输入和预期输出的示例。错误消息在哪里?
  • Goluch 先生应该看到这个吗? :)

标签: string function haskell char head


【解决方案1】:

您可以利用失败的模式匹配在列表解析和Data.List.tails 函数中的工作原理:

import Data.List (tails)

someFun :: String -> String
someFun s = [x | 'Z':_:_:x:_ <- tails s]

tails 函数为您提供列表的所有尾部(请记住,String 只是 Char 的列表),例如:

λ: tails "Overflow"
["Overflow","verflow","erflow","rflow","flow","low","ow","w",""]

模式('Z':_:_:x:_) 匹配任何以Z 开头并且大小至少为四个字符的字符串。对于每个模式匹配,提取Z 之后三个位置的字符。

关于它的神奇之处在于,当模式失败(例如对于不以Z 开头或太短的尾部)时,元素会被静默跳过并且不会t 对结果做出贡献 - 正是您所要求的。

【讨论】:

    【解决方案2】:

    阅读head和tail。使用它们更容易。当你的第一个列表的长度小于4时结束循环。

    someFun [] rs = rs
    someFun xs rs
    | (length xs) < 4 = rs
    | (head xs) == 'Z' = someFun (tail xs) (rs ++ [head (tail (tail (tail xs)))])
    | otherwise = someFun (tail xs) rs
    

    【讨论】:

      【解决方案3】:

      规范并不完全清楚,但听起来你想收集输入中'Z'之后三个位置出现的所有字符,以便从

      "BUZZARD BAZOOKA ZOOM"
      

      我们得到

      "RDKM"
      

      如果没有对问题进行更清晰的表述,就很难给出准确的建议。但我希望我能帮助你克服一些小烦恼,这样你就可以接触到问题的实际逻辑。

      让我们从类型开始。你有

      someFun :: String => String -> String -> String
      

      =&gt; 的左侧是类型表达式的属性 的位置,通常涉及可以代表许多类型的变量,例如Eq a(意味着无论a 是什么类型,我们可以测试相等性)。 String 是一个类型,而不是一个属性,所以它不能站在=&gt; 的左边。算了吧。这给了

      someFun  :: String -- input
               -> String -- accumulating the output (?)
               -> String -- output
      

      尚不清楚您是否真的需要蓄能器。假设你知道

      "ZARD BAZOOKA BOOM"  -- "DKM", right?
      

      你能计算输出吗

      "ZZARD BAZOOKA BOOM"  -- "RDKM"
      

      ?只是在前面多了一个'R',对吧?您正在使用尾递归来下一件事,而通常更容易考虑应该做什么。如果您知道列表尾部的输出,那么请说出整个列表的输出。为什么不直接将输入映射到输出,所以

      someFun :: String -> String
      

      现在,模式匹配,从最简单的模式开始

      someFun s = undefined
      

      你能看到足够的输入来确定输出吗?显然不是。输入是空的还是有第一个字符很重要。分为两种情况。

      someFun ""      = undefined
      someFun (c : s) = undefined   -- c is the first Char, s is the rest of the String
      

      第一个字符是否为'Z' 也很重要。注意Char 使用 引号,String 使用 引号:它们是不同的类型。

      someFun ""         = undefined
      someFun ('Z' : s)  = undefined   -- the first Char is Z
      someFun (c : s)    = undefined
      

      'Z' 的情况下,您还想确保s 至少有三个字符,我们关心第三个字符,所以

      someFun ""                         = undefined   -- input empty
      someFun ('Z' : s@(_ : _ : d : _))  = undefined   -- first is 'Z' and d is 3 later
      someFun (c : s)                    = undefined   -- input nonempty
      

      @ 是一个“as 模式”,允许我将整个尾部命名为 s 并检查它是否匹配 (_ : _ : d : _),抓住 'Z' 之后的第三个字符。

      到目前为止,我没有考虑输出,只是我需要查看输入。让我们弄清楚输出必须是什么。在第一种情况下,空输入给出空输出

      someFun ""                         = ""
      someFun ('Z' : s@(_ : _ : d : _))  = undefined   -- first is 'Z' and d is 3 later
      someFun (c : s)                    = undefined   -- input nonempty
      

      在其他两种情况下,我们可以假设someFun s 已经告诉我们列表尾部的输出,所以我们只需要弄清楚如何完成整个列表的输出。在最后一行中,尾部的输出正是我们想要的。

      someFun ""                         = ""
      someFun ('Z' : s@(_ : _ : d : _))  = undefined   -- first is 'Z' and d is 3 later
      someFun (c : s)                    = someFun s
      

      但如果我们发现d 位于初始'Z' 之后的三个位置,我们需要确保d 位于输出的开头。

      someFun ""                         = ""
      someFun ('Z' : s@(_ : _ : d : _))  = d : someFun s
      someFun (c : s)                    = someFun s
      

      只是检查:

      *Main> someFun "BUZZARD BAZOOKA ZOOM"
      "RDKM"
      

      关键的想法是弄清楚如何根据各个部分的输出来表达整个输入的输出:它是什么,而不是要做什么 .在这里,您可以假设尾部的输出 s 已正确计算,因此您只需要确定是否有任何额外的东西要返回。

      【讨论】:

        【解决方案4】:

        目前还不清楚您要做什么,但可以编译:

        someFun :: String -> String -> String
        someFun "" (r:rs) = (r:rs)
        someFun (a:b:c:d:xs) (r:rs)
            | a == 'Z' = someFun xs ((r:rs)++[d])
            | otherwise = someFun (b:c:d:xs) (r:rs)
        

        String =&gt; 用于类型类约束,您不需要。 dChar,而 (++) 是在列表中定义的(在这种情况下是 Chars)。

        你的函数有不完整的模式匹配,所以你也可以定义那些,这将简化现有的情况:

        someFun :: String -> String -> String
        someFun _ [] = error "Empty string"
        someFun "" s = s
        someFun ('Z':b:c:d:xs) s = someFun xs (s++[d])
        someFun (_:b:c:d:xs) s = someFun (b:c:d:xs) s
        someFun _ _ = error "String was not in the expected format"
        

        要将其显示在屏幕上,您可以使用putStrLnprint

        displaySomeFun :: String -> String -> IO ()
        displaySomeFun s1 s2 = putStrLn (someFun s1 s2)
        

        【讨论】:

        • 感谢您的回复,它确实有效,但我不确定如何使用它,当我得到这样的字符串时:let zdanie = "(C (N (Z 'p')) (A (K (Z 'p') (Z 'q')) (Z 'r')))" let x = someFun zdanie print $ x -- 这应该打印列表 ['p', 'q', 'r' ]
        • 如果你运行someFun "(C (N (Z 'p')) (A (K (Z 'p') (Z 'q')) (Z 'r')))" "",它会返回"ppqr",不是吗?
        • 如何将答案打印到屏幕上?当我点击 print $ someFun "(C (N (Z 'p')) (A (K (Z 'p') (Z 'q')) (Z 'r')))" "" 我得到编: "字符串不是预期的格式"
        • @user3235761 - 您可以使用 putStrLnprint - 查看更新。
        • 我点击了 displaySomeFun "(C (N (Z 'p')) (A (K (Z 'p') (Z 'q')) (Z 'r')))" " " 并且仍然得到 prog: 空字符串。我只是复制了你的答案,所以这很奇怪。
        【解决方案5】:

        Lee 展示了如何编译它。 还有一些话要说: 您必须提供更多模式案例,例如,如果您尝试运行 someFun "" ""someFun "A" "ABCD",则会收到错误消息

        第一个改进:将(r:rs) 更改为rs,您从不使用r,因此您可以将其更改为更一般的情况(这将修复someFun "" "" 上的错误)。 另一件事是,您不会在具有一个、两个或树元素的列表上进行模式匹配。 您可以添加someFun _ rs = rs,这样在这些情况下什么都不会发生。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-10-15
          • 1970-01-01
          • 2012-01-06
          • 2016-08-03
          • 2012-01-12
          • 2020-03-19
          • 2017-07-22
          • 2014-06-30
          相关资源
          最近更新 更多