如何在没有递归的情况下迭代字符串?

时间:2012-02-14 04:30:01

标签: haskell

isTogether' :: String -> Bool
isTogether' (x:xs) = isTogether (head xs) (head (tail xs))

对于上面的代码,我想浏览字符串中的每个字符。我不允许使用递归。

3 个答案:

答案 0 :(得分:5)

在Haskell中,String只是一个字符列表([Char])。因此,所有正常的高阶列表函数(如map)都适用于字符串。因此,您可以使用最适合您的问题的高阶函数。

请注意,这些函数本身是递归定义的;实际上,如果没有显式递归或使用直接或间接递归的函数,就无法在Haskell中查看整个列表。

答案 1 :(得分:4)

isTogether' (x:xs) = isTogether (head xs) (head (tail xs))

如果我做得对,你有兴趣从某些字符串中获取重要的字符串。因此,例如,对于abcd,您需要使用(a,b), (b,c), (c,d)(Char,Char) -> Bool函数测试Char -> Char -> Bool

Zip可能会对您有所帮助:

> let x = "abcd"
> let pairs = zip x (tail x)
it :: [(Char, Char)]

对于某些f :: Char -> Char -> Bool函数,我们可以获得uncurry f :: (Char, Char) -> Bool

然后使用[Bool]轻松获得map (uncurry f) pairs :: [Bool]结果值。

答案 2 :(得分:1)

要在没有递归的情况下执行此操作,您将需要使用更高阶函数或列表理解。我不明白你想要完成什么,所以我只能给出通用的建议。你可能会想要其中一个:

map :: (a -> b) -> [a] -> [b]

Map将一种类型的列表转换为另一种类型。使用map可以对列表的每个元素执行相同的操作,给定一个函数可以对列表中的各种事物进行操作。

filter :: (a -> Bool) -> [a] -> [a]

Filter获取列表和谓词,并为您提供一个新列表,其中只包含满足谓词的元素。只需使用这两个工具,您就可以做一些非常有趣的事情:

import Data.Char

map toUpper (filter isLower "A quick test") -- => "QUICKTEST"

然后你有各种各样的折叠。折叠实际上是用于在某种类型上进行递归的通用高阶函数,因此使用它需要一些习惯,但是你可以在带有折叠的列表上完成几乎任何递归函数。 foldr的基本类型如下所示:

foldr :: (a -> b -> b) -> b -> [a] -> b

它需要三个参数:归纳步骤,基本情况和您想要折叠的值。或者,在较少的数学术语中,您可以将其视为采用初始状态,采用下一项的函数以及生成下一状态的先前状态以及值列表。然后它返回它到达的最终状态。您可以使用折叠执行一些非常令人惊讶的事情,但是假设您要检测列表是否包含两个或更多相同项目的运行。这很难用map和filter表示(不可能?),但递归很容易:

hasTwins :: (Eq a) => [a] -> Bool
hasTwins (x:y:xs) | x == y    = True
hasTwins (x:y:xs) | otherwise = hasTwins (y:xs)
hasTwins _                    = False

嗯,你可以用这样的折叠来表达这个:

hasTwins :: (Eq a) => [a] -> Bool
hasTwins (x:xs) = snd $ foldr step (x, False) xs
  where
    step x (prev, seenTwins) = (x, prev == x || seenTwins)

所以我在这个折叠中的“状态”是先前的值以及我们是否已经看到了一对相同的值。该函数没有显式递归,但我的步骤函数将当前x值传递给下一次通过状态调用作为前一个值。但是你不必对你拥有的最后一个状态感到满意;此函数将第二个值从状态中取出并将其作为总返回值返回 - 这是布尔值,无论我们是否看到彼此相邻的两个相同值。