isTogether' :: String -> Bool
isTogether' (x:xs) = isTogether (head xs) (head (tail xs))
对于上面的代码,我想浏览字符串中的每个字符。我不允许使用递归。
答案 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值传递给下一次通过状态调用作为前一个值。但是你不必对你拥有的最后一个状态感到满意;此函数将第二个值从状态中取出并将其作为总返回值返回 - 这是布尔值,无论我们是否看到彼此相邻的两个相同值。