3

リスト内包表記は、再帰に比べてほとんど不可能だと思います。「te1234ST」などの文字列を取得して「TEST」を返そうとしています。簡単に思えますが、制限があります。isAlpha などの Haskell 定義済み関数の使用は許可されておらず、リスト内包表記でなければなりません。

私がこれまでに費やしてきた時間の割にはひどいものです:

    convertAllToUpper :: String -> String
    convertAllToUpper xs = [n |n <- xs, check n == True]

          -- This may not even be allowed, and I know it's incorrect anyway
    check :: n -> Bool
    check (n:ns)
        | n `elem` ['a'..'z']       = True
        | n `elem` ['A'..'Z']       = True
        | otherwise         = False

私はこれを機能させようとしているだけで、小文字を大文字に変更することについてまだ心配し始めていません。

正しい方向のポイントは非常に高く評価されます。

編集: 使用できない下位から上位への変換について言及する必要があります: if, then, else. 内包表記と演算子をリストするだけです。

4

4 に答える 4

9

問題は、次の 2 つのサブ問題に分けることができます。

  1. 英字のみを選択する (「a」と「z」または「A」と「Z」の間の文字)
  2. 小文字を大文字に変換します。

前者は、フィルター、または (リスト内包表記では) 選択された要素の条件で実行できます。Unicode (および ASCII) では、小文字は大文字の後に来るので、文字が 'a' より小さいかどうかを簡単にチェックして、大文字かどうかを判断できます (文字であることがわかったら)。すべてのアルファベット文字は英語です。 - アルファベット順。たとえば、小文字は 'a' と 'z' の間 (両端を含む) です。

Data.Char (chr、ord) の場合:

f xs = [ if x < 'a' then x else chr $ ord x + ord 'A' - ord 'a'
         | x <- xs, (x >= 'a' && x <= 'z') || (x >= 'A' && x <= 'Z') ]

Prelude のみの場合 (ただし、Data.Map を使用して記述した方がよいでしょう):

f xs = [ if x < 'a' then x else maybe x id $ lookup x charMap
         | x <- xs, (x >= 'a' && x <= 'z') || (x >= 'A' && x <= 'Z') ]
  where charMap = zip ['a' .. 'z'] ['A' .. 'Z']

もちろん、正しい方法は標準ライブラリを使用することです。これは、いくつかの基本関数を使用して非常に簡単に実行できます。

-- with Data.Char (toUpper, isAlpha)
f xs = [ toUpper x | x <- xs, isAlpha x ]

これは多くの点で非常に優れています: おそらくより高速で、ASCII 入力に依存していません — あらゆる Unicode 文字を扱うことができます (そして、原則としてあらゆるローカリゼーション: たとえば、トルコ語の 'i' は大文字で 'İ' として正しく大文字化されます)。 'I' は 'ı' の大文字であるため、ASCII や英語ロケールの 'I' ではありませんが、Haskell 実装がこれを正しく実装しているかどうかはわかりません)。

リスト内包表記は再帰のサブセットであることに注意してください: フォームの再帰関数を書くことができれば:

f []       = []
f (x : xs) = if p x then g x : f xs else f xs 

次の形式のリスト内包表記に機械的に変換できます。

f xs = [ g x | x <- xs, p x ]

ただし、多変数リスト式を使用することもできますが、これは再帰的に表現するのが少し複雑です。したがって、再帰を理解していれば、リスト内包表記は簡単に理解できるはずです。

于 2013-02-16T23:19:02.190 に答える
1

これを見てください

-- The char you want to take into account
valid_char = ['a'..'z'] ++ ['A'..'Z']

-- To filter the other char
valid xs = [ x | x<- xs, v <- valid_char, x==v]

-- Transform the list of valid char in a list of valid upper char 
to_upper xs = [if (x==fst n) then snd n else x | x <- xs, n <- (zip ['a'..'z'] ['A'..'Z']), (x==fst n) || (x==snd n)]  

-- composition of the two preceding function
convert = to_upper . valid

そしてテスト

$ convert "test1234ST" => "TEST"
于 2013-02-17T00:05:22.517 に答える
1

あらかじめ定義された関数を完全に避けるのは難しいです - 前置きで定義されたリスト関数を使用しても問題ないと思いますか? リスト内包表記にスローされるすべてのものを次に示します。

upcaseLetters :: String -> String
upcaseLetters cs =
  [d | c <- cs
     , c `elem` (['a'..'z'] ++ ['A'..'Z']) -- test if `c` is a latin letter.
     , let d = if c `elem` ['A'..'Z']      -- test if `c` is uppercase
               then c
               -- Index into [A..Z] using the index of `c` in [a..z]
               else ['A'..'Z'] !! head [i | (i, x) <- zip [0..] ['a'..'z']
                                          , x == c]]

ただし、これらのリスト関数を使用するのはごまかしだと感じるかもしれません。また、本物のプログラマーは外部依存を避けます。この哲学に従って、リスト内包表記内でプレリュードのかなりの部分をブートストラップできます。

upcaseLetters :: String -> String
upcaseLetters cs =
  [toUpper' c | c <- cs
     , let foldr' _ z []     =  z
           foldr' f z (x:xs) =  f x (foldr' f z xs)

           True ||| _ = True
           _ ||| True = True
           _ ||| _    = False

           elem' e xs = foldr' (|||) False [e==x | x <- xs]

           head' (x:_) = x

           zip' (a:as) (b:bs) =  (a, b) : zip' as bs
           zip' _ _           =  []

           isAlpha' x = x `elem'` (['a'..'z'] ++ ['A'..'Z'])

           isUpper' x = x `elem'` ['A'..'Z']

           toUpper' e
             | isUpper' e = e
             | otherwise  = ['A'..'Z'] !! head' [i | (i, x) <- zip' [0..] ['a'..'z']
                                                   , x == e]
     , isAlpha' c
     ]

このアプローチは、折り畳みの明快さとリスト内包表記の読みやすさを兼ね備えています。

残念ながら、言語の設計上の見落としにより、Haskell はリスト内包表記の本体内で新しいデータ型を宣言できません。これは、プレリュードの Char、String、および Bool 型への依存を取り除くことができないことを意味します。

そうでなければ、[toUpper x | x <- xs , isAlpha x]あなたが通常望むものです。

于 2013-02-16T23:22:48.097 に答える
0
convertAllToUpper :: String -> String
--                                  This is equivalent to `check n`
convertAllToUpper xs = [n |n <- xs, check n == True]

-- This is a type declaration. Types always begin with a uppercase letter.
-- Therefore "n" can't be valid in this position.
-- You want "check :: Char -> Bool"
check :: n -> Bool
-- This is a pattern match for a list, but you call it with single
-- characters. That can't work.
check (n:ns)
    -- Again, this is the "if foo then true else false" pattern.
    -- This is redundant.
    | n `elem` ['a'..'z']       = True
    | n `elem` ['A'..'Z']       = True
    | otherwise         = False
-- A shorter version of check
check' :: Char -> Bool
check' x = x `elem` ['a'..'z'] ++ ['A'..'Z']

-- when you have written the function `toUpper`
-- (or taken it from Data.Char`) you can simply apply it to
-- every element in the comprehension.
convertAllToUpper' xs = [toUpper n | n <- xs, check n]
于 2013-02-16T23:16:56.813 に答える