これは私がやりたいことです:
INPUT: [1,2,3,-1,-2,-3]
OUTPUT:[1,1,1,-1,-1,-1]
私はこれを試しました:
signNum (x:n) = map(if x>0
then 1
else -1)n
ロジックのどこで間違いを犯したか教えてもらえますか?
最初の問題はmap
、関数を期待することです。if
したがって、ステートメントをラムダでラップする必要があります。ただし、これでも希望どおりにはなりません。リストを先頭と末尾に分割する代わりに、関数をリスト全体にマップする必要があります。
map
関数を取り、それを各要素に適用するだけであることを忘れないでください。1
各要素を または のいずれかに変換したいので-1
、リストに適切な関数をマップするだけです。
したがって、最終的には次のようになります。
sigNum ls = map (\ x -> if x > 0 then 1 else - 1) ls
この場合、関数をより小さな部分に分割する方がおそらく簡単です。
最も低いレベルでは、1 つのsignum
数値の を計算できます。つまり、次のようになります。
signum :: (Num a, Ord a) => a -> a
signum x = if x > 0 then 1 else -1
これを取得したら、他の関数の場合と同様に、数値のリストで使用できます。
signNum ls = map signum ls
(ps とはどういうsignum 0
意味ですか?現在の定義にはsignum 0 = -1
.
このケースを含めるために関数を拡張する必要がある場合は、ガードを使用することをお勧めします。
signum x | x < 0 = -1
| x == 0 = 0
| otherwise = 1
またはケースステートメント:
signum x = case compare x 0 of
LT -> -1
EQ -> 0
GT -> 1
)
あなたのコメントは、あなたがこれを理解したいと思っていることを示唆しています。
あなたが理解してこれをしたいのなら、あなたはすることができます
signNum ls = [ if x>0 then 1 else -1| x <- ls ]
...しかし、右辺に条件を置くことはできません
brokenSignNum ls = [ 1| x <- ls, x > 0 ]
条件を右側に置くと、条件を満たさないものはすべて削除されるため、マイナスはすべて無視されます。これにより、要素が置き換えられるのではなく、リストが短くなります。やってみよう
brokenSignNum2 ls = [ 1| x <- ls, x > 0 ] ++ [ -1| x <- ls, x <= 0 ]
これは元のリストと同じ長さですが、すべてのポジティブが先頭にあります。
要約: この条件式は左側に配置する必要があります。これは、置換が発生する唯一の場所であるためです。右側では、削除が行われます。
if ステートメントは 0 を負としてカウントすることに注意してください。本当にそれが欲しいですか?おそらく、数値の符号を個別に定義したほうがよいでしょう。
sign x | x == 0 = 0 -- if x is zero, use zero
| x > 0 = 1 -- use 1 for positives
| x < 0 = -1 -- use -1 for negatives
workingSignNum1 ls = [sign x | x <- ls]
しかしsign
、(ほぼ) function と同じなsignum
ので、それを使用することもできます
workingSignNum2 ls = [signum x | x <- ls]
これで、基本的に「リスト全体x
を置き換える」という意味の構文がたくさんあります。私たちはそのようなことをよくするので、それを行う関数を書くことができます:sign x
ls
replaceUsing :: (a -> b) -> [a] -> [b]
replaceUsing f xs = [f x | x <- xs]
しかし、それを行う機能がすでにあります!と呼ばれていmap
ます。したがって、リストで map を使用できます。
quiteSlickSignNum :: Num a => [a] -> [a]
quiteSlickSignNum ls = map signum ls
またはさらに滑らか:
slickSignNum :: Num a => [a] -> [a]
slickSignNum = map signum
それが私がそれを定義した方法です。
sign
たのですか?signum
sign
は数値を受け取り、1
、0
、またはを返します-1
が、 の型は1
何ですか?
さて、1
型を持っているNum a => a
ので、任意の数値型で使用できます。これは
sign
、任意の型の数値を取り、任意の型の数値を返すことを意味するため、その型は
sign :: (Num a,Num b) => a -> b
そのため、私のバージョンのsign
は別のタイプを提供できます。試してみると、 ではなくが3 * sign 4.5
得られるので、整数を取得できますが、 を取得すると が得られるため、10 進数型も取得できます。対照的に、3
3.0
3.14 * sign 7.4
3.14
signum :: Num a => a -> a
そのため、指定した型のみを返すことができます -3 * signum 4.5
を与えます3.0
。
エラーメッセージ "no instance for Num
" は、新しい Haskeller が解読するのが最も難しいものの 1 つです。まず、記述しようとしている関数の完全にポリモーフィックな型シグネチャを次に示します (同じエラーを取得するために、これをソース ファイルに追加しました)。
signNum :: (Ord a, Num a) => [a] -> [a]
エラーを見つける
現在、コンパイルエラーメッセージは次のように表示されています。
prog.hs:3:17 のリテラル `1' から生じるコンテキスト (Ord a, Num a) から (Num (a -> a)) を推測できませんでした。
エラー メッセージが問題の場所を示していることに注意してください。「リテラル 1」 atfile_name.hs:line_number:column_number
が問題であると書かれています。
signNum (x:n) = map(if x>0
then 1 -- <-- here's the problem! (according to that message)
else -1)n
エラーを理解する
現在、エラー メッセージはいくつかの可能な修正も示唆していますが、「no instance for Num
」に遭遇するたびに、提案された「可能な修正」はほとんどの場合間違っているため、無視してください。Num
(GHC が、このような に関連するエラー メッセージをより適切に提供してくれることを願っています)。
エラー メッセージの内容を思い出してください。
推論できませんでした (Num (a -> a)) ... リテラル `1' から生じる ...
1
これが意味することは、コンテキストが type の何かを期待する場所にリテラルを配置することです
a -> a
。1
は明らかに関数ではないため、コンテキストが間違っているか、数字の 1 が間違っています。
では、リテラル 1 のコンテキストは何ですか?
エラーを見つける(正確に)
(if x > 0
then <<hole>>
else -1)
Haskell の if ステートメントは値を生成します。if ステートメントの分岐は同じ型でなければならず、if ステートメントの型は分岐の型によって決まります。
ここで、もう一方のブランチの値-1
は数値です。したがって<<hole>>
、 が同じ型、つまり数値を持つことが期待されます。これは明らかに問題ではありません (1は数値であるため) ので、その式のコンテキストを見てみましょう。
map <<hole>> n
このmap
関数は、最初の引数として関数を想定しています。<<hole>>
ただし、が数値を生成することはわかっています。ユーレカ!ここに不一致がmap
あります。関数が必要な場所に数値を指定しています。
エラーの修正
問題が何でどこにあるかが正確にわかったので、明白な解決策はmap
、数値ではなく関数を与えることです。詳細については、他のさまざまな回答を参照してください。