4

私は Haskell で作業を開始していますが、私が行おうとしているこの等価チェックがうまくいきません。

私は関数を持っています。countLetter a [b] cここaで、はchar、bはcharのリストc、intです。(型宣言は正常に渡されました。) しかし、この式で問題が発生しました。

if a == head(b)

次のメッセージが表示されます。

Type error in application

*** Expression     : a == head b
*** Term           : a
*** Type           : [a]
*** Does not match : a
*** Because        : unification would give infinite type

必要に応じて、私のコード全体は次のとおりです。

countLetter :: char -> [char] -> int

countLetter a [b] c = if null b

                       then []
                       else
                       if a == head(b)
                       then countLetter a tail(b) c+1
                    else
                    countLetter head(b) tail(b) c

ヘルプやアドバイスをいただければ幸いです。ありがとうございました。

4

3 に答える 3

9

まず第一に、Haskellのタイプは大文字で始まります。型シグネチャで小文字で始まる識別子を使用する場合、それらは型変数として解釈されます。したがって、タイプchar -> [char] -> intはと同じですがa -> [a] -> b、これは関数にとって適切なタイプではありません。あなたの欲しいChar -> [Char] -> Int

次に[]、タイプの有効な値ではありませんInt。型署名を修正すると、あいまいさの少ない用語でこれを通知するエラーメッセージが生成されます。

また、これはnull b型エラーであるため、型エラーであることbCharわかります(関数の2番目の引数は型[Char]であり、パターンと照合し、そのリストに含まれる単一のもの[b]にバインドします)。引数としてのChar。bCharnull

最後のポイントを少し明確にするために:

関数の2番目の引数はリストです。パラメータリストに書き込むこと[b]で、その引数をパターンと照合します[b]。言い換えればcountLetter a [b] c = blabla、書くことは書くことと同じです:

countLetter a theList c =
    case theList of
        [b] -> blabla

つまり、「関数の2番目の引数は、単一の要素を含むリストである必要があり、その要素はb「」と呼ばれる必要があります。それはあなたが言いたいことではありません。あなたが言いたいのは、「関数の2番目の引数(ちなみに、リストです)はb」と呼ばれることです。そのためにあなたは単に書くcountLetter a b c = blabla

タイプリストのパラメータは、他のタイプのパラメータと同じように指定する必要はありません。

于 2012-12-19T19:00:01.533 に答える
2

発生する別のエラーは次のとおりです。

countLetter head(b) tail(b) c

これは、ルール上、

countLetter head b tail b c

つまり、countLetter 関数を 5 つの引数で呼び出しますが、3 つ (最初の式によると) または 2 つ (型シグネチャによると) しか必要としません。(これは、コンパイラが非常に不満を抱くもう 1 つのポイントです。)

おそらくこれが必要です:

countLetter (head b) (tail b) c

同じく:

countLetter a tail(b) c+1

と同じです

(countLetter a tail b c) + 1

しかし、あなたはおそらく欲しい:

countLetter a (tail b) (c+1)
于 2012-12-19T23:24:48.883 に答える
1

関数を修正する方法を提供する他の回答とは別に、これをより構成的なスタイルで書くことを検討することをお勧めします。具体的には、他の標準関数を構築して関数を作成する方法を探します。テスト文字と等しくないものをすべてリストから削除する関数があるとします。

myFilter :: Char -> [Char] -> [Char
myFilter = ...

を使用するmyFilterと、チェックしている要素のみを含むリストが残ります。この時点で、を使用lengthしてリストの長さを取得できます。

countLetter :: Char -> [Char] -> Int
countLetter a b = length $ myFilter a b

だから今あなたはただ定義する必要がありますmyFilter、それは標準のプレリュード関数で行うことができますfilter

myFilter a b = filter (==a) b

これほど小さい関数の場合、独自の定義を作成する価値はほとんどありません。

countLetter a b = length $ filter (== a) b

次に、これをghciで定義して、関数で検出されるタイプを確認します。countLetter

Prelude> let countLetter a b = length $ filter (== a) b
Prelude> :t countLetter
countLetter :: Eq a => a -> [a] -> Int

Char見えない!実装は文字である要素に依存せず、それらが等しいかどうかを比較できることだけに依存します(これはあなたのアプローチにも当てはまります)。したがって、ghciはそれを計算されたタイプに反映します。Charただし、を代入すると、これがまさに必要なタイプであることがわかりますa

多くの関数型プログラマーは、このアプローチを見つける傾向があります。つまり、特に推論しやすい小さなパーツを構成して関数を構築するため、非常に一般的です。特にリストを操作する場合は、再帰を使用する代わりに、、、、またはfoldmapなどのいわゆる高階関数を使用して実装を記述できるかどうかを確認する必要があります。filter再帰が最も明確な方法である場合もありますが、関数の合成がより良いアプローチであることがよくあると思います。

于 2012-12-20T06:58:58.450 に答える