使用char或int拆分字符串/列表

时间:2012-12-09 18:07:11

标签: haskell

我正在尝试在Haskell中创建一个函数,将字符串拆分为某个字符串和一个特定数字的列表。

为了做到这一点,splitAt函数正是我需要的数字,但我不能给这个函数一个字符。

E.g。

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和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放在tupple的错误一侧。您可以将succ添加到c,但如果char是Z,那么结果会是什么。

是否有一种简单的方法可以修改

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

工作?

谢谢!

4 个答案:

答案 0 :(得分:2)

您可以使用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 :: 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])

答案 1 :(得分:2)

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

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

fromMaybe :: a - >也许是 - >一个

  

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

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

答案 2 :(得分:1)

鉴于c :: Chars :: String,您可以将其写为

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

  1. 如果c未加入s
  2. ,则会收到例外情况
  3. 你遍历s两次
  4. Maybe替代方案是

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

    你可以在我的例子中设置“else value”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
    

    然后,如果(s, "")中没有c,则会s

答案 3 :(得分:0)

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

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. 扔掉所有Bool个。我们不再需要它们了。