Yorumlanmış aritmetik


9

Bilinen bir gerçek şu ki, yeterli dil uzantılarını (ghc) açarsanız, Haskell dinamik olarak yazılmış bir yorumlanmış dil haline gelir! Örneğin, aşağıdaki program toplama işlemini uygular.

{-# Language MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, UndecidableInstances #-}

data Zero
data Succ a

class Add a b c | a b -> c
instance Add Zero a a
instance (Add a b c) => Add (Succ a) b (Succ c)

Bu artık Haskell'e benzemiyor. Birincisi, nesneler üzerinde çalışmak yerine türler üzerinde çalışıyoruz. Her sayı kendi tipidir. Fonksiyonlar yerine tip sınıflarımız var. İşlevsel bağımlılıklar, bunları türler arasında işlev olarak kullanmamızı sağlar.

Peki kodumuzu nasıl çağırıyoruz? Başka bir sınıf kullanıyoruz

class Test a | -> a
 where test :: a
instance (Add (Succ (Succ (Succ (Succ Zero)))) (Succ (Succ (Succ Zero))) a)
  => Test a

Bu test, tipini 4 + 3 tipine ayarlar . Bunu ghci'de açarsak, testbunun gerçekten tip 7 olduğunu göreceğiz :

Ok, one module loaded.
*Main> :t test
test :: Succ (Succ (Succ (Succ (Succ (Succ (Succ Zero))))))

Görev

İki Peano rakamını (negatif olmayan tamsayılar) çarpan bir sınıf uygulamanızı istiyorum. Peano rakamları yukarıdaki örnekte aynı veri türleri kullanılarak oluşturulacaktır:

data Zero
data Succ a

Ve sınıfınız da yukarıdaki gibi değerlendirilecektir. Sınıfınıza dilediğinizi adlandırabilirsiniz.

Baytlara ücretsiz olarak istediğiniz ghc dil uzantılarını kullanabilirsiniz.

Test Durumları

Bu test senaryoları sınıfınızın adlandırıldığını varsayar M, isterseniz başka bir ad verebilirsiniz.

class Test1 a| ->a where test1::a
instance (M (Succ (Succ (Succ (Succ Zero)))) (Succ (Succ (Succ Zero))) a)=>Test1 a

class Test2 a| ->a where test2::a
instance (M Zero (Succ (Succ Zero)) a)=>Test2 a

class Test3 a| ->a where test3::a
instance (M (Succ (Succ (Succ (Succ Zero)))) (Succ Zero) a)=>Test3 a

class Test4 a| ->a where test4::a
instance (M (Succ (Succ (Succ (Succ (Succ (Succ Zero)))))) (Succ (Succ (Succ Zero))) a)=>Test4 a

Sonuçlar

*Main> :t test1
test1
  :: Succ
       (Succ
          (Succ
             (Succ
                (Succ (Succ (Succ (Succ (Succ (Succ (Succ (Succ Zero)))))))))))
*Main> :t test2
test2 :: Zero
*Main> :t test3
test3 :: Succ (Succ (Succ (Succ Zero)))
*Main> :t test4
test4
  :: Succ
       (Succ
          (Succ
             (Succ
                (Succ
                   (Succ
                      (Succ
                         (Succ
                            (Succ
                               (Succ
                                  (Succ
                                     (Succ (Succ (Succ (Succ (Succ (Succ (Succ Zero)))))))))))))))))

Tipik teknik röportajdan ilham alır


Dil uzantıları ücretsiz mi? Öyleyse hangileri?
Potato44

@ Potato44 Oh evet. Tüm dil uzantıları ücretsizdir.
Ad Hoc Garf Hunter

1
Heh ... Bu yazı olmasa da meme-y gibi görünüyor.
Sihirli Ahtapot Urn

Yanıtlar:


9

130 121 bayt

Ørjan Johansen sayesinde -9 bayt

type family a+b where s a+b=a+s b;z+b=b
type family a*b where s a*b=a*b+b;z*b=z
class(a#b)c|a b->c
instance a*b~c=>(a#b)c

Çevrimiçi deneyin!

Bu toplama (+)ve çarpma için kapalı tip aileleri tanımlar (*). Daha sonra tip ailesinin dünyasından daktilo sınıfı prolog dünyasına dönüştürmek için bir tip eşitliği ile birlikte tip ailesini (#)kullanan bir tip sınıfı tanımlanır (*).


3
Eğer denklemleri takas varsa, yerine Zerogöre z.
Ørjan Johansen

1
@ ØrjanJohansen Tamamlandı. Birisi için 9 bayt, benim için 9 bayt.
Potato44

Ben tür aileleri nasıl kullanılacağını bilmiyorum, ama belki böyle bir fonksiyon bu Tanımlamak gerek kalmaz +yararlıdır?
Lynn

@Lynn daha uzun çıkıyor. TIO
Potato44

1
@WheatWizard Sadece yorumda gönderdiğim kodun daha uzun çıktığı için aslında cevabınızın kuyruk özyinelemeli versiyonu olduğunu fark ettim.
Potato44

6

139 bayt

class(a+b)c|a b->c;instance(Zero+a)a;instance(a+b)c=>(s a+b)(s c)
class(a*b)c|a b->c;instance(Zero*a)Zero;instance((a*b)c,(b+c)d)=>(s a*b)d

Çevrimiçi deneyin!

Bir tür işleci tanımlar *. Prolog programına eşdeğerdir:

plus(0, A, A).
plus(s(A), B, s(C)) :- plus(A, B, C).
mult(0, _, 0).
mult(s(A), B, D) :- mult(A, B, C), plus(B, C, D).

Potato44 ve Hat Wizard 9 bayt kurtardı. Teşekkürler!


Veri bildirimlerinizi bayt toplamınıza saymanız gerekmez. Bir şansım olduğunda soruda bunu daha net hale getireceğim
Ad Hoc Garf Hunter

Ayrıca fyerine bir general kullanabilirsiniz düşünüyorum Succ.
Ad Hoc Garf Hunter

1
İki nokta üst üste dikerek 9 bayt tasarruf edebilirsiniz.
Potato44

Sanırım Hat Wizard da 6 değil 9 kurtardı. Üç Succ olayı vardı.
Potato44

1

Aile Sürümü, 115 bayt

type family(a%b)c where(a%b)(s c)=s((a%b)c);(s a%b)z=(a%b)b;(z%b)z=z
class(a#b)c|a b->c
instance(a%b)Zero~c=>(a#b)c

Çevrimiçi deneyin!

Bu, potato44 gibi kapalı tip bir aile kullanır . Diğer cevabın aksine sadece 1 tip aile kullanıyorum.

type family(a%b)c where
  -- If the accumulator is Succ c:
  -- the answer is Succ of the answer if the accumulator were c
  (a%b)(s c)=s((a%b)c)
  -- If the left hand argument is Succ a, and the right hand is b
  -- the result is the result if the left were a and the accumulator were b
  (s a%b)z=(a%b)b
  -- If the left hand argument is zero
  -- the result is zero
  (z%b)z=z

Bu, üç tipte bir operatörü tanımlar. Aslında uygular (a*b)+c. Sağ el argümanımızı toplama eklemek istediğimizde, bunu akümülatöre koyduk.

Bu, hiç tanımlamamıza engel olur (+). Teknik olarak, bu aileyi,

class Add a b c | a b -> c
instance (Succ Zero % a) b ~ c => Add a b c

Sınıf Sürümü, 137 bayt

class(a#b)c d|a b c->d
instance(a#b)c d=>(a#b)(f c)(f d)
instance(a#b)b d=>(f a#b)Zero d
instance(Zero#a)Zero Zero
type(a*b)c=(a#b)Zero c

Çevrimiçi deneyin!

Bu sınıf sürümü, aile sürümüne biraz önem verir, ancak yine de buradaki en kısa sınıf sürümünden daha kısadır. Benim aile versiyonumla aynı yaklaşımı kullanıyor.


Güzel, tip ailenizin matematiksel olarak * b + c uyguladığını görüyorum. "Bölme" den söz etmek "ekleme" anlamına mı geliyor?
Potato44

btw, şu anda kendi spesifikasyonunuzu ihlal ediyorsunuz. "İki Peano rakamını çarpan bir sınıf uygula" Şu anda bir sınıfın olmadığı bir şey Constraintolsa da, bu tür bir durum . Bu nedenle, ya özellikleri güncellemeli ya da tür eşanlamı yerine bir sınıfı kullanan forma geri dönmelisiniz. Tür eşanlamlısını kullanacak olsaydım cevabımı 96 bayta düşürebilirdim, bu yüzden beni senden bir bayt daha kurtardı
Potato44

@ Potato44 Bir sınıfın sadece bir çelişkiyle sonuçlanan bir şey olduğu izlenimindeydim. Belki de bu sorudaki netlik eksikliğinden kaynaklanıyordu. O zaman 115 cevabıma geri döneceğim.
Ad Hoc Garf Hunter
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.