2011-01-21 21 views
4

私は 'real world haskell'の半分で、私の最初のコードを書いています。非常に遠くになっていませんでした...クラスとインスタンスを理解しようとしています

基本的に、リストと数値の計算を行うために、doubleとdoubleリストの配列構文を実装しようと考えていました。 「クラス」はCLOSのジェネリック関数のようなものだと思いますか?私は配列とスカラーを追加するための関数arplusで 'クラス'を作り、それをスカラーのために作った。今私はリストのために同じことをする方法を知らない。 (計算を行うのは簡単ですが、リストバージョンでは「Double」の代わりに何を入れるべきか分かりません)。

class Narray a where 
    arplus :: a -> a -> a 

instance Narray Double where 
    arplus a b = a + b 

次に、どのように混合バージョンを作成しますか?配列スカラ配列とスカラー配列?代わりに

class Narray a where 
    arplus :: a -> b -> a 

は、私のようなものが必要でしょうか?

私が書こうとしているコードの例として、以下のsbclコードを参照してください。 (注 - これはlispの配列で行いましたが、私はhaskellのリストでそれをやっていました入力された。実際にも」問題の観点から重要ではありません。)

;; Generic add for arrays and scalar 
(defgeneric .+ (a b)) 

;; Scalar-scalar 
(defmethod .+ ((a double-float) (b double-float)) (+ a b)) 

;; Array-array 
(defmethod .+ ((a SB-KERNEL::SIMPLE-ARRAY-DOUBLE-FLOAT) 
     (b SB-KERNEL::SIMPLE-ARRAY-DOUBLE-FLOAT)) 
    (dotimes (i (array-total-size a)) 
     (setf (row-major-aref a i) 
     (+ (row-major-aref a i) 
      (row-major-aref b i)))) a) 

;; Array-scaler 
(defmethod .+ ((a SB-KERNEL::SIMPLE-ARRAY-DOUBLE-FLOAT) (b double-float)) 
    (dotimes (i (array-total-size a)) 
     (setf (row-major-aref a i) 
     (+ (row-major-aref a i) 
      b))) a) 

;; Scalar-array 
(defmethod .+ ((a double-float) (b SB-KERNEL::SIMPLE-ARRAY-DOUBLE-FLOAT)) 
    (dotimes (i (array-total-size b)) 
     (setf (row-major-aref b i) 
     (+ a 
      (row-major-aref b i)))) b) 


;; Just to demo the code 
(defun indgen (n) 
    (let ((r (make-array n :element-type 'double-float))) 
    (dotimes (i n) 
     (setf (row-major-aref r i) (coerce i 'double-float))) r)) 


* (load "arrays.lisp") 

T 
* (.+ (indgen 6) 10d0) 

#(10.0d0 11.0d0 12.0d0 13.0d0 14.0d0 15.0d0) 
* (.+ (indgen 6) (indgen 6)) 

#(0.0d0 2.0d0 4.0d0 6.0d0 8.0d0 10.0d0) 
* 
+4

私はReal World Haskellが、[Learn You A Haskell](http://learnyouahaskell.com/)を使って素材を補完すると、本当にうまく動作することを発見しました。 –

+2

あなたがしようとしていることは、haskellの "あなたが何を意味するか"の哲学とうまくいっていないでしょう。配列配列の追加(zip)は、スカラー配列の追加(マッピング)とは異なる概念です。したがって、2つの別々の演算子が必要になります。現在の規約では、ベクトル引数の側で '^'が使用されます。 '^ + ^'は配列配列、 '+ ^'はスカラ配列です。 – luqui

+0

AFAIK Haskellは複数のタイプのディスパッチをサポートしていないので、 'class Narray a b c where arplus :: a - > b - > c'のように書くことができますが、すべての型を明示的にマークする必要があります。 – adamax

答えて

3

は全体の質問に答えることはできませんが、リストのバージョンのために、私はこれが動作するようになった:

私も(ヌムA)=>を回してみました
main = [1, 2, 3] `arplus` [4, 5, 6] 

class Narry a where 
    arplus :: a → a → a 

instance (Num a) ⇒ Narry [a] where 
    arplus = zipWith (+) 

[A]のNumのインスタンスに、素晴らしい結果が得られました。あなただけもちろんこの

main = do print $ [1, 2, 3] * 3 -- [3, 6, 9] 
      print $ 3 * [1, 2, 3] -- [3, 6, 9] 
      print $ 3 - [2, 4, 6] -- [-1, 1, 3] 
      print $ [2, 4, 6] + 7 -- [9, 11, 13] 
      print $ abs [-2, 4, -3] -- [2, 4, 3] 
      print $ [1, 2, 3] + [4.3, 5.5, 6.7] -- [5.3, 7.5, 9.7] 
      print $ [1, 2, 3] * [3, 4, 5] -- [3, 8, 15] 

ようにそれを試してみることができます

instance (Num a) ⇒ Num [a] where 
    (+) = zipWith (+) 
    (*) = zipWith (*) 
    (-) = zipWith (-) 
    negate = map negate 
    abs = map abs 
    signum = map signum 
    fromInteger = repeat∘fromInteger 

あなたがリストをダブル算術演算を行いたい場合、あなたはいくつかのより多くのもののための定義を記述する必要があります。ここで私が定義した範囲は、私が実証した以上のものではないとは思うが、ここで何ができるかわからない。

また、これはNumのリストをNumのインスタンスにする唯一の方法ではありません。 zipWith(*)よりも良い選択肢があります。

[編集] Fractionalのインスタンスをリストにすることは簡単ですし、もっと便利な機能が追加されています。

これにより、リストと部分的なやり取りが可能になり、自動的に作品が分割されます。

ghci> [3, 6, 9]/3 
[1.0,2.0,3.0] 
ghci> 9/[1, 2, 3] 
[9.0,4.5,3.0] 
ghci> 1.2 + [0, 1, 2] 
[1.2,2.2,3.2] 

大きな力は大きな責任です。必要な場合にのみこれらを使用してください。この動作が必要ない場合は、番号とリストで+を呼び出そうとすると、コンパイラに叫ぶのがいいですね。

+0

Danさんに感謝します。素晴らしいもの。 –

3

基本的に、私は上の数値計算を行うことができますので、私はダブル、ダブルリストの配列構文を試してみて、実装しようと思いましたリスト。 「クラス」はCLOSのジェネリック関数のようなものだと思いますか?

ハスケルの型クラスは、「アドホック多型」と呼ばれることもあります。つまり、関数が複数の型で動作することができますが、それぞれ異なる動作を実行できます。彼らは他のほとんどの言語のものと完全に匹敵しませんが、CLOSの「ジェネリック関数」はかなりよく似ています。

次に、どのように混合バージョンを作成しますか?配列スカラ配列とスカラー配列?

あなたの配列とスカラーは異なる型であると仮定します。型クラスには単一の型パラメータがあるため、明らかに問題があります。 GHCを使用している場合は、基本的に言わない限り、基本的には... MultiParamTypeClassesという言語拡張があります。

残念ながら、さまざまな理由から実際には痛みを感じることがありますが、この場合はどちらかの引数の順序で混合バージョンを使用したいが、結果は両方の配列にする必要があります。これを行う最も直接的な方法は、結果を第3の型のパラメータにすることです。しかし、型パラメータは互いに独立しているため、Haskellは配列にarplusが適用され、スカラが配列であることを知ることができません。これに対処するために、他の拡張も存在しますが、いくつかのタスクでは事態が急速に複雑になる可能性があります。

今、私は私が正しくあなたの例を読んでいることを確認するためのLispと少しもさびだけど、ここで私はあなたが望む考えるもので刺しだ:

{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE FunctionalDependencies #-} 

class Narray a b c | a b -> c where 
    arplus :: a -> b -> c 

instance Narray Double Double Double where 
    arplus = (+) 

instance Narray Double [Double] [Double] where 
    arplus x = map (x +) 

instance Narray [Double] Double [Double] where 
    arplus x y = map (+ y) x 

instance Narray [Double] [Double] [Double] where 
    arplus = zipWith (+) 

あなたは拡張子を調べることができます私は詳細については、GHCのドキュメントで使用しました。

これは、実際には必ずしも良い考えではなく、単純な多重ディスパッチスタイルのオーバーロードを行う方法の例として上記を取っておいてください。@ luquiのコメントによれば、このタイプクラスは概念的に異なる操作、ほとんど利益にはならない。

全体的なデザインの面白さがあなたにとって有益なのかどうかは分かりませんが、この場合はタイプクラスを全く気にしません。

+0

あなたの答えに感謝camccann。私はあなたの最後のコメントがすべてそうだと思うかもしれない。あなたが新しい言語(興味深いもの)を学んでいるときは、最も難しい部分は新しい考え方を学ぶことです。あなたがこれらすべての拡張機能を追加しなければならなかったという事実は、私がこれをすべて間違っていると思っていること、そして私がhaskellを書くのではなく戦っていることを示唆しています。スカラーやリストのための演算子のための何かを作るためにここで正しい考え方プロセスは何でしょうか? –

関連する問題