2

データがあり、これが整数なのか文字列なのかわからなかったが、(+1) を適用したかった場合、それが整数であれば素晴らしいが、文字列であれば何もしない場合、どのように処理しますか?これ?出番はここNothingですか?

4

3 に答える 3

8

Haskell 関数は厳密に型指定されています。つまり、入力と出力の型を指定します。そもそも複数の型の値を受け入れるためにも、Eitherそれらを同じ型内に保持するために使用する必要があります。したがって、たとえば、Stringまたはのいずれかを受け取りたい場合Integer、関数には次の型が必要です。

f :: Either String Integer -> ...

次に、関数を記述する方法は、 でパターン マッチを行い、Either受け取った値の型を確認することです。次のように記述します。

-- str is a String
f (Left  str) -> ... 
-- int is an Integer
f (Right int) -> ...

したがって、必要なことを行う最も簡単な方法は、数値が の場合にのみ増分しInteger、そのままにしておくことStringです。次のように記述します。

-- Don't do anything if it is a string
f (Left  str) = Left  str
-- Increment it if it is an integer
f (Right int) = Right (int + 1)

上記の関数の型を推論するようにコンパイラに依頼すると、次のようになります。

f :: Either String Int -> Either String Int

Haskell にはfmapFunctorクラスから使用する上記のボイラープレートを回避するための優れたトリックがあります。これにより、次のように、半分にあるものを完全に無視しながら、Right半分だけに関数を自動的に書き込むことができます。EitherLeft

f = fmap (+1)

上記の関数の型を推測すると、実際には次のようになります。

f :: (Functor f, Num a) => f a -> f a

aしかし、私たちの場合、 toIntegerfto を設定することで型を特殊化できますEither String:

f :: Either String Int -> Either String Int

つまり、多数あるクラスEither Stringのインスタンスの一例です。Functor

ただし、この関数を整数または文字列で使用するには、まずそれらをLeftまたはRightコンストラクターでラップする必要があることに注意してください。たとえば、これらは型チェックされません。

f 1   -- WRONG!
f "a" -- WRONG!

しかし、これらは次のようになります。

f (Right  1) -- Correct!
f (Left "a") -- Correct!

これは、たとえば、整数と文字列のリストがある場合、次のように記述する必要があることを意味します。

list = [Right 1, Left "a", Right 2]

リスト内で整数と文字列を混在させようとすると、型エラーが発生することに注意してください。

list = [1, "a", 2] -- WRONG!

次にf、最初の正しいものをマッピングして、次listを取得できます。

map f list = [Right 2, Left "a", Right 3]
于 2012-09-17T19:29:38.783 に答える
5

タイプを混ぜたくない

あなたは言う

リストがあるとしますが、リスト内の正確なタイプを知らずにいくつかの関数を適用したい場合、このリストは ["a"] または [1] の可能性があります。

Haskell では、そのようなリストを作成できません。リスト内のすべての要素は同じ型でなければなりません。[1,2,3]またはを使用できますが、 は使用でき["a","cheese","4"]ません["a",1]。Haskell は、リスト内の要素で機能する関数のみを適用するようにします。Haskell とあなたの両方が、リスト内の要素の型をいつでも解決できます。この型の知識は静的型付けと呼ばれ、最初は不必要に厳格に見えますが、実際には非常に柔軟です。ゴールデン ルールは次のとおりです。コンパイラは、常にどのようなデータがあるかを認識しています。

型の問題をどのように処理できますか?

このようなタイプの問題は発生しません。これが静的型付けの利点です。データの型を間違えたり、データの型を変換したりするプログラムはコンパイルされません。コードを実行する前に間違いを見つけることができます。

文字列と数値が混在するデータ ストリームがあるとします。最初に質問する必要があるのは、「それらをどうしたいのか、本当にそれらを混ぜ合わせる必要があるのか​​?」ということです。それらを分離するより良い方法はありますか?たとえば、Haskell では、完全な機能を備えた静的に型指定されたパーサーを作成するのは非常に簡単です (これが必要な場合は parsec を検索してください)。


新規: 非整数を無視する

(質問のタイトルを読み直すと、おそらくこれがあなたの求めているものです。)

非整数を無視するのは非常に簡単です。あなたが言及したように、このようにNothingとその対応物を使用できますJust

myData = ["nan","45","3454.5","that's not an int, it's a float!","4","6"]

maybeInts :: [String] -> [Maybe Integer] -- type signature needed so Haskell knows you want Integers
maybeInts = map maybeRead 

これは便利な関数maybeReadを使用して各値を変換しますが、代わりにコードをコピーして貼り付ける必要がimport Network.CGI.Protocol (maybeRead)ありData.Maybeます。

maybeRead :: Read a => String -> Maybe a
maybeRead = fmap fst . listToMaybe . reads 
  -- take all parses, grab the first one, ignore the remaining text

あなたが得る価値maybeInts myData

[Nothing, Just 45, Nothing, Nothing, Just 4, Just 6]

Nothingしかし、おそらくあなたはそのすべて/Just綿毛を望んでおらず、むしろ整数を取得したいだけです. フーグルするとモジュールから[Maybe a] -> [a]見つかるので、定義しましょうcatMaybesData.Maybeimport Data.Maybe

intsOnly :: [String] -> [Int]
intsOnly = catMaybes.map maybeRead  -- read them, ignore Nothings and unwrap Justs

だから、あなたがintsOnly myData得るとき

[45,4,6]

そして、それらすべてに1つ追加したい場合は、次のことができます

map (+1) (intsOnly myData)

これは

[46,5,7]

要求に応じ。

整然とした型の混合

整数と文字列を混在させる必要がある場合は、次のことができます

[Left 5, Right "a", Right "hello", Left 6]

これはIntegers またはStrings のいずれかのリストなので、タイプEither Int Stringは ですが、数値とテキストだけでなく、整数、文字列、時間、または文字列のリストが必要な場合もあります。あなたは自分自身を転がすことができます:

data MyStuff = MyInt Int | MyString String | MyDate DateTime | MyList FilePath [String] 
  deriving Show

次のようなリストにすることができます

mystuff = [MyInt 6,  MyString "Hello",  MyString "MyString isn't a great name", 
    MyList "C:/temp/stuff.txt" ["yeah","give","them","problem-specific", "names"], 
    MyInt 12,  MyString "instead"]

(Haskell では大文字と小文字が区別されることに注意してください。MyStuffは型でMyStringあり、型コンストラクタ (データを型にラップする一種の関数) でmystuffあり、変数です。すべての変数は定数です!)

あなたのデータはMy___タグでどのようにラップされるのでしょうか? おそらく、ファイル内またはネットワーク接続などからのテキスト全体のロードとして開始され、パーサーはそれをint、文字列、日付、および文字列のリストに分割し、それに沿ってタグ付けしました(非常に簡単です)。

...そして、取得するデータに応じて定義が異なる関数を作成することで、それらを分離できます。

report :: MyStuff -> IO ()
report (MyInt n) = mapM_ print [1..n]   -- turn the numbers 1..n into print commands, then combine them
report (MyString xs) = print xs         -- just print the string.
report (MyList filename contents) 
    = writeFile filename (unlines contents) -- write them on seperate lines in the file
report _ = return ()                    -- if it's anything else, ignore it and do nothing.

気が向いたらやってみようかな

mapM_ report mylist

そして、いくつかの数字、いくつかの文字列を出力し、ファイルを保存し、さらにいくつかの数字を出力し、最後に文字列を出力します。

于 2012-09-17T20:56:42.400 に答える
1

2 つのタイプのいずれかになる値がある場合は、Eitherその値のタイプを使用できます。typeまたはtype のいずれかである value もEither同様に、パラメーターとして他の 2 つの型を取ります。2 つのケースは、2 つの異なるコンストラクターによって識別され、どちらかの左 ( ) または右 ( ) 型を意味します。あなたの場合、使用します。値を操作するには、パターン マッチングを使用できます。例えば、Either a babLeftRightabEither String Integer

foo :: Either String Int -> Either String Int
foo (Left s) = Left s
foo (Right n) = Right (n+1)

質問で尋ねた変換を実装します。

于 2012-09-17T19:29:18.547 に答える