要完全按照您的要求进行(但将函数名称的首字母更正为小写),我可以定义
connect :: Char -> [(Char,Char)] -> [(Char,Char,Int)]
connect c pairs = [(a,b,n)|((a,b),n) <- zip pairs [0..], a == c]
如果
pairing = zip "ABACUS" "YELLOW"
我们得到
ghci> connect 'A' pairing
[('A','Y',0),('A','L',2)]
但是,我认为使用zip3 压缩一次而不是两次会更整洁:
connect3 :: Char -> String -> String -> [(Char,Char,Int)]
connect3 c xs ys = filter (\(a,_,_) -> a==c) (zip3 xs ys [0..])
相当于
connect3' c xs ys = [(a,b,n)| (a,b,n) <- zip3 xs ys [0..], a==c]
它们都按您的意愿工作:
ghci> connect3 'A' "ABACUS" "YELLOW"
[('A','Y',0),('A','L',2)]
ghci> connect3' 'A' "ABACUS" "AQUAMARINE"
[('A','A',0),('A','U',2)]
在 cmets 中,您说您希望反过来获得配对。
这一次,使用单子do 表示法最为方便,因为列表是单子的一个示例。
connectEither :: (Char,Char) -> String -> String -> [(Char,Char,Int)]
connectEither (c1,c2) xs ys = do
(a,b,n) <- zip3 xs ys [0..]
if a == c1 then return (a,b,n) else
if b == c2 then return (b,a,n) else
fail "Doesn't match - leave it out"
我使用了fail 函数来排除不匹配的内容。以if、if 和fail 开头的三行越来越缩进,因为从 Haskell 的角度来看,它们实际上是一行。
ghci> connectEither ('a','n') "abacus" "banana"
[('a','b',0),('a','n',2),('n','u',4)]
在这种情况下,它没有包含('n','a',2),因为它只检查一种方式。
我们可以通过重用现有函数来实现这两种方式:
connectBoth :: (Char,Char) -> String -> String -> [(Char,Char,Int)]
connectBoth (c1,c2) xs ys = lefts ++ rights where
lefts = connect3 c1 xs ys
rights = connect3 c2 ys xs
这给了我们想要得到的一切:
ghci> connectBoth ('a','n') "abacus" "banana"
[('a','b',0),('a','n',2),('n','a',2),('n','u',4)]
但不幸的事情不止一次:
ghci> connectBoth ('A','A') "Austria" "Antwerp"
[('A','A',0),('A','A',0)]
所以我们可以使用来自Data.List 的nub 来摆脱它。 (在文件顶部添加import Data.List。)
connectBothOnce (c1,c2) xs ys = nub $ connectBoth (c1,c2) xs ys
给予
ghci> connectBothOnce ('A','A') "ABACUS" "Antwerp"
[('A','A',0),('A','t',2)]