解释Haskell代码

时间:2017-11-28 18:29:58

标签: haskell

我得到了这段代码并被告知解释它的逻辑,有没有人知道发生了什么,是Haskell的新手。

cpfx :: [[Char]] -> [Char]
cpfx [] = []
cpfx [x] = x
cpfx (x:xs) = cpfx' (x:xs) 0

cpfx' :: [[Char]] -> Int -> [Char]
cpfx' [x] _ = []
cpfx' (x:xs) n
    | ifMatch (x:xs) n = x!!n : cpfx' (x:xs) (n+1)
    | otherwise = []

ifMatch :: [[Char]] -> Int -> Bool
ifMatch [x] _ = True
ifMatch [x,y] n = x!!n == y!!n
ifMatch (x:y:xs) n
    | x!!n == y!!n = ifMatch xs n
    | otherwise = False

我无法理解cpfx,cpfx'和ifMatch正在做。

1 个答案:

答案 0 :(得分:1)

直接拍摄每个功能并查看它。让我们从下往上开始,因为他们互相使用。

ifMatch :: [[Char]] -> Int -> Bool

所以ifMatch获取Chars(或字符串列表)和Int列表的列表,并返回true / false值。现在让我们看一下模式匹配

[x] _      = True          -- one element list and any number is True
[x, y] n   = x!!n == y!!n  {- two element list is true if the character at index
                              @n@ is the same in both lists -}
(x:y:xs) n                 -- three+ element list guards against...
  | x!!n == y!n            --   if the characters at index @n@ are equal...
      = ifMatch xs n       --   ... then recurse
  | otherwise = False      --   otherwise, give me a False.

总之,您可以看到ifMatch应该检查传递给它的所有字符串在索引n处是否具有相同的字母。它相当于:

ifMatch [] _ = True
ifMatch xs n = let c = head xs !! n in
               foldr ((&&) . (==c) . (!!n)) True xs

虽然它实际上似乎有一个小错误。它仅检查每对字符串在索引n处是否具有相同的字母,所以

ifMatch ["This", "That", "In", "Onward"] 1 == True
--        (^  ==   ^)  && (^  == ^)
cpfx' :: [[Char]] -> Int -> [Char]

所以cpfx'获取一个Chars列表(或一个字符串列表)和一个Int列表,并返回一个Chars(或一个字符串)列表。我们来看看模式匹配:

cpfx' [x] _    = []       -- one element list and any number is the empty list
cpfx' (x:xs) n            -- multiple element list guards against...
  | ifMatch (x:xs) n      --   if all @x:xs@ share an element at @n@...
    = x!!n :              --   ...add x!!n and...
      cpfx' (x:xs) (n+1)  --   ...recurse to the next index
  | otherwise = []        --   otherwise, empty list.

因此,这会抓取x中的字符,直到x:xs中的所有字符串中的字符不再匹配,并返回该前缀字符串。

cpfx :: [[Char]] -> [Char]

从字符列表(或字符串列表)列表到字符列表(或字符串)。

cpfx []     = []              -- empty string gives empty string
cpfx [x]    = x               -- one-element list gives its only element
cpfx (x:xs) = cpfx' (x:xs) 0  {- anything else gives the shared prefix starting
                                 at index zero -}

总而言之,我们有三个函数可以协同工作,为您提供存在于该列表中所有字符串开头的字符串列表头部的最长子字符串。

import Data.Maybe (mapMaybe)

safeIndex :: [a] -> Int -> Maybe a
safeIndex xs n | length xs > n = Just $ xs !! n
               | otherwise     = Nothing

allEq :: Eq a => [a] -> Bool
allEq [] = True
allEq [_] = True
allEq (x:xs) = all (==x) xs

prefix :: [String] -> String
prefix xss@(x:_) = map snd $ takeWhile pred $ zip [0..] x where
  pred :: (Int, Char) -> Bool
  pred (n, _) = (allEq . mapMaybe (flip safeIndex n)) xss

testData :: [String]
testData = ["Hello", "Hello, World!", "Hello, Universe!", "Hello everybody!", "Hell's to you, then!"]

main :: IO ()
main = do
  let p = prefix testData
  putStrLn "Should be \"Hell\""
  putStrLn p

transpose

更容易
import Data.List (transpose)

prefix' :: [String] -> String
prefix' xss@(x:_) = take (length $ takeWhile allEq transposed) x where
  transposed = transpose xss

或略高效

lengthWhile :: (a -> Bool) -> [a] -> Int
lengthWhile _ [] = 0
lengthWhile pred (x:xs) | pred x    = 1 + lengthWhile pred xs
                        | otherwise = 0

prefix'' :: [String] -> String
prefix'' xss@(x:_) = take (lengthWhile allEq transposed) x where
  transposed = transpose xss