次のような文字列のリストがあります。
xs = ["xabbaua", "bbbaacv", "ggfeehhaa", "uyyttaccaa", "ibbatb"]
リスト内で、 と vocel の後に 2 つの b が続き、その後に任意の文字と母音が続く文字列のみを検索したいと考えています。このような単純な一致は Haskell でどのように行われますか。正規表現よりも良い解決策はありますか? 誰かが例を手伝ってくれますか? ありがとう。
従来のフィルター関数を任意の正規表現ライブラリと組み合わせて使用できます。あなたのパターンは十分に単純なので、これはどの正規表現ライブラリでも機能します:
filter (=~ "bb.[aeiuy]") xs
Haskell の正規表現の紛らわしい部分は、すべての特定のライブラリと希望する複数の結果の型 (Bool、String、Int. ..)。基本的な使用法では、ほとんどの場合、意図したとおりに機能するはずです(tm)。特定のニーズに対しては、regex-posix で十分です (また、haskell プラットフォームに付属しているため、通常はインストールする必要はありません)。したがって、インポートすることを忘れないでください:
import Text.Regex.Posix
このチュートリアルでは、他のニーズがある場合に正規表現 API の基本を説明する必要があります。現在は少し古くなっていますが、基本は同じままで、正規表現ベースの詳細のみが変更されています。
1 つのアプローチは、小さなパターン マッチング言語を構築し、それを Haskell に埋め込むことです。
あなたの例では、パターンは基本的に文字仕様のリストです。値がそのような仕様として機能する抽象文字のタイプを定義しましょう。
data AbsChar = Exactly Char | Vowel | Any
文字が仕様に一致するかどうかを伝える「インタープリター」と一緒に:
(=?) :: AbsChar -> Char -> Bool
Exactly c' =? c = c == c'
Vowel =? c = c `elem` "aeiou"
Any =? c = True
たとえば、Vowel =? 'x'
は を生成False
しますが、Vowel =? 'a'
は を生成しTrue
ます。
実際、パターンは単なる抽象文字のリストです。
type Pattern = [AbsChar]
次に、文字列のプレフィックスが特定のパターンに一致するかどうかをテストする関数を作成します。
matchesPrefix :: Pattern -> String -> Bool
matchesPrefix [] _ = True
matchesPrefix (a : as) (c : cs) = a =? c && matchesPrefix as cs
matchesPrefix _ _ = False
例えば:
> matchesPrefix [Vowel, Exactly 'v'] "eva"
True
> matchesPrefix [Vowel, Exactly 'v'] "era"
False
一致するプレフィックスに制限するのではなく、単語内の任意の場所に一致させたいため、次の関数は文字列のすべての末尾セグメントのプレフィックスに一致します。
containsMatch :: Pattern -> String -> Bool
containsMatch pat = any (matchesPrefix pat) . tails
tails
モジュールにある関数を使用しますData.List
が、この説明を自己完結型にするために、自分自身も簡単に定義できます。
tails :: [a] -> [[a]]
tails [] = [[]]
tails l@(_ : xs) = l : tails xs
例えば:
> tails "xabbaua"
["xabbaua","abbaua","bbaua","baua","aua","ua","a",""]
さて、最後に、一致するセグメントを含むリストからすべての文字列を選択する、探していた関数は次のように簡単に記述されます。
select :: Pattern -> [String] -> [String]
select = filter . containsMatch
あなたの例でそれをテストしましょう:
> let pat = [Vowel, Exactly 'b', Exactly 'b', Any, Vowel]
> select pat ["xabbaua", "bbbaacv", "ggfeehhaa", "uyyttaccaa", "ibbatb"]
["xabbaua"]
まあ、これは最良の方法ではないかもしれませんが、あなたはこの関数を試すことができます:
elem' :: String -> String -> Bool
elem' p xs = any (p==) $ map (take $ length p) $ tails xs
使用法:
filter (elem' "bb") ["xxbbaua", "bbbaacv", "ggfeehhaa", "uyyttaccaa", "bbbaab"]
また
bbFilter = filter (elem' "bb")
Regex を使用することに絶対に反対する場合は、パターン マッチングと再帰だけで行うことができますが、見栄えは悪くなります。
xs = ["xabbaua", "bbbaacv", "ggfeehhaa", "uyyttaccaa", "ibbatb"]
vowel = "aeiou"
filter' strs = filter matches strs
matches [] = False
matches str@(x:'b':'b':_:y:xs)
| x `elem` vowel && y `elem` vowel = True
| otherwise = matches $ tail str
matches (x:xs) = matches xs
呼び出しfilter' xs
は返さ["xabbaua"]
れますが、これは必要な結果であると私は信じています。