【问题标题】:Split string/list with a char or int用 char 或 int 分割字符串/列表
【发布时间】:2012-12-09 18:07:11
【问题描述】:

我正在尝试在 Haskell 中创建一个函数来拆分某个 char 的字符串和某个数字的列表。

为此,splitAt 函数正是我需要的数字,但我不能用这个函数给出一个字符。

例如

splitAt 5 [1,2,3,4,5,6,7,8,9,10]

给予

([1,2,3,4,5],[6,7,8,9,10])

这正是我在元组左侧的 5 所需要的。 但现在我想用char 和一个字符串来做到这一点。但是splitAt 只接受和int 作为第二个参数。我想要

splitAt 'c' "abcde"

导致

("abc", "de")

我在寻找方向

splitAt (findIndex 'c' "abcde") "abcde"

但是函数findIndex返回Maybe Int类型的东西,splitAt需要Int。然后我尝试了以下

splitAt (head (findIndices (== 'c') "abcde")) "abcde"

这是一个可能的解决方案,但它返回以下内容

("ab","cde")

c 在元组的错误一侧。您可以将 succ 添加到 c 但如果 char 是 Z 会是什么结果。

有没有简单的修改方法

splitAt (findIndex 'c' "abcde") "abcde"

工作?

【问题讨论】:

  • 如果您使用break:: (a -> Bool) -> [a] -> ([a], [a]) 而不是splitAt,则不需要findIndex
  • @SebastianPaaskeTørholm 但你在右边而不是左边得到'c'

标签: haskell


【解决方案1】:

您可以使用findIndex,只需解开Maybe 并添加一个:

import Data.List

splitAfter :: (a-> Bool) -> [a] -> ([a],[a])
splitAfter this xs = case findIndex this xs of
    Nothing -> (xs,[])
    Just n -> splitAt (n+1) xs

例如给予

*Main> splitAfter (=='c') "abcde"
("abc","de")

Maybe 是一种方便的数据类型,用于以一种易于恢复的方式编码失败。甚至还有一个函数maybe :: b -> (a -> b) -> Maybe a -> b 使用默认值和一个函数分别处理这两种情况:

splitAfter' :: (a-> Bool) -> [a] -> ([a],[a])
splitAfter' this xs = maybe (xs,[]) 
                  (\n -> splitAt (n+1) xs) 
                  (findIndex this xs)

这也有效。例如

*Main> splitAfter' (==5) [1..10]
([1,2,3,4,5],[6,7,8,9,10])

【讨论】:

    【解决方案2】:

    您可以使用fromMaybe 函数从 Maybe 中获取结果,例如:

    splitlist = splitAt (fromMaybe 0 (findIndex 'c' "abcde") "abcde")
    

    fromMaybe :: a -> Maybe a -> a

    fromMaybe 函数采用默认值和 Maybe 值。如果 Maybe is Nothing,它返回默认值;否则,它 返回 Maybe 中包含的值。 (source)。

    默认值设置为 0,如果您的 findIndex 返回 Nothing,则 splitAt 的结果将是 ("",list),对于相同的情况但默认值设置为 length list,最终结果将是 (list,"") .

    【讨论】:

      【解决方案3】:

      给定c :: Chars :: String,你可以写一些为

      splitAt ((1+) $ fromJust $ findIndex (==c) s) s
      

      但是

      1. 如果c 不属于s,则会出现异常
      2. 你遍历s 两次

      Maybe 替代方案是

      maybe Nothing (\x -> splitAt (1+x) s) (findIndex (==c) s)
      

      您可以设置“其他值”(在我的示例中为Nothing)。

      你可以编写自己的函数

      splitAt' :: Char -> String -> (String, String)
      splitAt' _ [] = ("", "")
      splitAt' c (x:xs) | c == x = ([c], xs)
                        | True   = (x:cs, ys) where (cs, ys) = splitAt' c xs
      

      然后,如果不是c,你会在s 中得到(s, "")

      【讨论】:

        【解决方案4】:

        这是一种不同的方式,它不涉及弄乱列表索引。

        break 几乎是您想要的。让我们重用它。您希望匹配元素包含在第一个输出列表的末尾,而不是第二个输出列表的开头。

        import Control.Arrow ((***))
        
        breakAfter :: (a -> Bool) -> [a] -> ([a], [a])
        breakAfter p xs = map fst *** map fst $ break snd (zip xs $ False : map p xs)
        

        这是如何工作的:

        1. 将我们的输入列表转换为对列表 (zip)。每对的第一个元素取自原始列表。该对的第二个元素是Bool,说明列表的previous 元素是否是我们要查找的元素。这就是为什么我们说False : map p xs --- 如果我们只是说map p xs,我们将完全重现break 的行为。在开头添加额外的False 很重要。
        2. 重用break。我们的条件编码在每对的第二个元素中。
        3. 扔掉所有Bools。我们不再需要它们了。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-07-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-03-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多