2012-05-16 21 views
15

İki matematik vektörü ekleyen bir işlev +++ istiyorum. herhangi n boyutlu vektör (yani bu çok [x, y] için çalışacak) karşılamak böyleceHaskell: Bir liste ve tuple arasında

(+++) :: (Num a) => [a] -> [a] -> [a] 
(+++) = zipWith (+) 

Ve:

Ben [x, y, z] ve kullanımı gibi vektörleri uygulayabilir.

Veya (x, y, z) ve kullanım gibi vektörleri uygulamak: Elbette

type Triple a = (a, a, a) 

merge :: (a -> b -> c) -> Triple a -> Triple b -> Triple c 
merge f (a, b, c) (x, y, z) = (f a x, f b y, f c z) 

(+++) :: (Num a) => Triple a -> Triple a -> Triple a 
(+++) = merge (+) 

bu biraz daha karmaşıktır, ancak tüm diğer vektör işlevleri yerine zaman, bu önemsizdir (yerine 40 50 satırlar).

Liste yaklaşımı ile ilgili sorun, 3B vektörü bir 3B vektörü ile ekleyebileceğimdir. Bu durumda, zipWith, 3B vektörlerinin z bileşenini basitçe keser. Bu mantıklı olsa da (daha büyük olasılıkla 2B vektörünü [x, y, 0]'a genişletmelidir), diğer işlevler için sessizce gerçekleşmesi sorunlu olabileceğini düşünüyorum. Tuple yaklaşımındaki problem, vektörü 3 bileşenle sınırlandırmasıdır.

Sezgisel, bunun matematiksel vektör bileşenlerinin sabit sayıda olduğundan, (x, y, z) olarak vektörleri temsil etmek daha mantıklı olacağını düşünürdüm ve gerçekten eksileri mantıklı (başa getirebilir) bir vektör bir bileşen yapmaz.

Diğer yandan, 3 boyutlu vektörlerden başka bir şeye ihtiyacım olamayacak kadar olası olmamasına rağmen, bunu bununla sınırlamak oldukça doğru görünmemektedir.

İstediğim şey, eşit uzunluktaki tuple'larda çalışan eşit uzunluktaki iki veya daha iyi işlevleri içeren işlevlerdir.

önerileri, pratiklik, ölçeklenebilirlik, zarafet, vb?

{-# LANGUAGE FlexibleInstances #-} -- needed to make tuples type class instances 

class Additive v where 
    (+++) :: v -> v -> v 

instance (Num a) => Additive (a,a) where 
    (x,y) +++ (ξ,υ) = (x+ξ, y+υ) 
instance (Num a) => Additive (a,a,a) where 
    (x,y,z) +++ (ξ,υ,ζ) = (x+ξ, y+υ, z+ζ) 
... 

Bu şekilde, eklenebilir değişken uzunlukta dizilerini ama derleme sırasında sağlanacaktır:

+0

http: // stackoverflow.com/questions/7220953/does-haskell-have-variadic-functions-tuples –

+0

Bu sorunun biraz eski olduğunu biliyorum, ancak [vektör-uzayı] 'na bir göz atmak isteyebilirsiniz (http: //hackage.haskell .org/paket/vektör-alan) paketi. –

cevap

-1

Landei'nin ve özgeçmişinin cevapları iyi (ikinize de teşekkürler), ve sanırım bu umduğum kadar basit olmayacaktı. Önerdiğim seçeneklerden herhangi birini yapmaya çalışıyorum, kullanıcı kodunun da bakması çok hoş olmayacak gibi gözüken karmaşık bir kod oluşturuyor.

Bence, sadece 3 boyutlu sadece vektörlerle tuple gitmeye ve çubukları kullanmaya karar verdim, çünkü listelerini kullanmaktan daha semantik olarak doğru görünüyor. Yine de, map, zipWith, sum ve diğerleri için yeniden etkili olmaya son veriyorum. Basitlikle uğraşmak istiyorum - sanki listeler gibi vektörleri düşünmek için zorlayıcı bir argümanım olsaydı, o zaman bu çözüm daha iyi çalışırdı (boyutları karıştırmamaya özen göstermemi sağladım)… Aslında vektörleri kullandığımda, bununla birlikte işlevler, değişken boyutlardan biri değil, bağımsız değişken olarak 3 boyutlu bir vektör alır ve Num a => [a] bunu uygulayamaz.

+0

'Veri'yi kullanın. Vector vektör paketinden veya 3B vektörleri olan "ACVector" paketinden. Bu kütüphaneler, zaman ve enerji tasarrufu sağlayarak yardımcı fonksiyonları tanımladı. – vivian

+3

Kitaplık kodu karmaşık olabilir, ancak 'tür' tanımları ve kolaylık yöntemleri gibi şeyler kullanarak kullanıcıyı oldukça iyi gizleyebilirsiniz. – Landei

14

kolay yolu bir tür sınıfında +++ operatörü koymak ve çeşitli tanımlama grubu örneklerini boyutları yapmaktır -her zaman her iki tarafın da aynı uzunlukta olması. Gerçek tip sınıfında senin merge gibi bir işlevi kullanmak için bu yaygınlaştırılması


da mümkündür: Bu durumda, (liste monad gibi) bir tür kurucusu olarak sınıf örneğini belirtmek gerekir.

class Mergable q where 
    merge :: (a->b->c) -> q a -> q b -> q c 

instance Mergable Triple where 
    merge f (x,y,z) (ξ,υ,ζ) = (f x ξ, f y υ, f z ζ) 

ve sonra basitçe

(+++) :: (Mergable q, Num a) => q a -> q b -> q c 
+++ = merge (+) 

Maalesef yapar oldukça eser tip eş kısmen değerlendirilecek olmayabilir çünkü.Sen

newtype Triple a = Triple(a,a,a) 

gibi yerine Triple bir newtype yapmak gerekir ve daha sonra

instance Mergable Triple where 
    merge f (Triple(x,y,z)) (Triple((ξ,υ,ζ)) = Triple(f x ξ, f y υ, f z ζ) 

tabii bakmak için çok güzel olarak hangi.

+3

@VladtheImpala: belki de [Japonca] tercih edersiniz (http://codegolf.stackexchange.com/a/4824/2183)? - Cidden, yerel değişkenleri Yunanca isimleriyle aramanın nesi yanlış? Kimseyi kendi kodlarına yazmaya zorlamaz, eğer Yunan alfabesini biliyorsanız, örn. zeta ile z, ve eğer yapmazsanız, keyfi latin harflerle karşılaştırıldığında çok az fark yaratır. – leftaroundabout

21

Tip seviyesi programlamayı kullanabilirsiniz. İlk önce her doğal sayıyı ayrı bir tür yapmalıyız. Doğal sayıların Peano tanımı takiben, Z0 ve S x

data Z = Z 
data S a = S a 

class Nat a 
instance Nat Z 
instance (Nat a) => Nat (S a) 

x + 1 Şimdi biz sadece bir liste sarmak için bir tür Vec kullanabilirsiniz, ancak Nat kullanarak büyüklüğü takip etmektir. Bunun için, smart constructorsnil ve <:>

data Vec a = Vec a [Int] 

nil = Vec Z [] 

infixr 5 <:> 
x <:> (Vec n xs) = Vec (S n) (x:xs) 

Şimdi iki vektörler aynı Nat olmasını gerektirir bir add fonksiyonu, tanımlayabilirsiniz (böylece modülünden veri yapıcısı Vec ihraç olmamalı) kullanın:

toList (Vec _ xs) = xs 
main = print $ toList $ add (3 <:> 4 <:> 2 <:> nil) (10 <:> 12 <:> 0 <:> nil) 
: Artık

add :: Nat a => Vec a -> Vec a -> Vec a 
add (Vec n xs) (Vec _ ys) = Vec n (zipWith (+) xs ys) 

Eğer uzunluk bilgi içeren bir vektör türü var

Elbette farklı uzunluktaki vektörlerin bulunması, bir derleme hatasına neden olacaktır.

Bu, anlaşılması kolay bir sürümdür, daha kısa, daha verimli ve/veya daha uygun çözümler vardır.

+0

"Daha verimli ve/veya daha uygun" çözümler merak ediyorum, kimse bazı göstergeler vermeye önem veriyor mu? – sinan

+0

"HList" ile benzer etkilere sahip olabileceğinizi düşünüyorum: http://hackage.haskell.org/packages/archive/HList/0.2.3/doc/html/Data-HList-HListPrelude.html – Landei

1

OP daha hafif bir yaklaşım istediği için ilişkili türleri kullanırdım.

class VecMath a b where 
    type Res a b :: * 
    (+++) :: a -> b -> Res a b 

instance Num a => VecMath (a,a,a) (a,a,a) where 
    type Res (a,a,a) (a,a,a) = (a,a,a) 
    (x1,y1,z1) +++ (x2,y2,z2) = (x1+x2, y1+y2, z1+z2) 

instance Num a => VecMath (a,a) (a,a,a) where 
    type Res (a,a) (a,a,a) = (a,a,a) 
    (x1,y1) +++ (x2,y2,z) = (x1+x2, y1+y2, z) 

instance Num a => VecMath (a,a,a) (a,a) where 
    type Res (a,a) (a,a,a) = (a,a,a) 
    -- (+++) analog 
instance Num a => VecMath (a,a) (a,a) where 
    type Res (a,a) (a,a) = (a,a) 
    -- ... 

Res

burada esasen 's argümanlar 'büyük' ​​türü sonuçlanan bir tür fonksiyonudur. Avantajı, VecMath yokmuş gibi düz eski tuplerle hala çalışabilmenizdir. Karanlık taraf, Res etki alanına yeni türler eklemeyi düşünürseniz, yazmanız gereken örneklerin üssel patlamasıdır. Daha fazla bilgi için bkz. this.