3

我正在尝试在 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只需要 andint用于第二个参数。我想

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"

工作?

4

4 回答 4

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 是一种方便的数据类型,用于以易于恢复的方式编码失败。甚至还有一个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])
于 2012-12-09T18:19:02.727 回答
2

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

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

fromMaybe :: a -> Maybe a -> a

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

将默认值设置为 0,如果您的 findIndex 返回 splitAtNothing的结果("",list),对于相同的情况,但默认值设置为length listfinally 结果,它将是(list,"")

于 2012-12-09T18:23:34.123 回答
1

给定c :: Charand s :: 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

然后,如果(s, "")不在c.s

于 2012-12-09T18:28:58.250 回答
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说明列表的前一个元素是否是我们正在寻找的元素。这就是为什么我们说False : map p xs--- 如果我们只是说map p xs,我们会准确地重现break. 在开始时坚持额外False是重要的一点。
  2. 重用break。我们的条件编码在每对的第二个元素中。
  3. 扔掉所有那些Bools。我们不再需要它们了。
于 2012-12-09T19:19:28.717 回答