Giải thích số học


9

Một thực tế ít được biết đến là nếu bạn bật đủ các phần mở rộng ngôn ngữ (ghc), Haskell sẽ trở thành một ngôn ngữ được dịch theo kiểu động! Ví dụ chương trình sau đây thực hiện bổ sung.

{-# 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)

Điều này thực sự không giống Haskell nữa. Đối với một thay vì hoạt động trên các đối tượng, chúng tôi hoạt động trên các loại. Mỗi số là loại riêng. Thay vì các hàm, chúng ta có các lớp loại. Các phụ thuộc chức năng cho phép chúng ta sử dụng chúng như các chức năng giữa các loại.

Vậy làm thế nào để chúng ta gọi mã của chúng tôi? Chúng tôi sử dụng một lớp học khác

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

Cái này đặt loại của testloại 4 + 3. Nếu chúng ta mở cái này trong ghci, chúng ta sẽ thấy đó testthực sự là loại 7:

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

Bài tập

Tôi muốn bạn thực hiện một lớp nhân hai số Peano (số nguyên không âm). Các số Peano sẽ được xây dựng bằng cách sử dụng cùng loại dữ liệu trong ví dụ trên:

data Zero
data Succ a

Và lớp học của bạn sẽ được đánh giá theo cách tương tự như trên. Bạn có thể đặt tên lớp của bạn bất cứ điều gì bạn muốn.

Bạn có thể sử dụng bất kỳ tiện ích mở rộng ngôn ngữ ghc nào bạn muốn mà không mất phí.

Các trường hợp thử nghiệm

Các trường hợp kiểm thử giả định rằng lớp của bạn được đặt tên M, bạn có thể đặt tên cho nó một cái gì đó khác nếu bạn muốn.

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

Các kết quả

*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)))))))))))))))))

Lấy cảm hứng từ Đánh máy phỏng vấn kỹ thuật


Phần mở rộng ngôn ngữ có miễn phí không? Nếu vậy cái nào?
Khoai tây44

@ Khoai tây ồ. Tất cả các phần mở rộng ngôn ngữ là miễn phí.
Ad Hoc Garf Hunter

1
Heh ... Bài đăng này có vẻ như meme-y mặc dù không phải vậy.
Bạch tuộc ma thuật Urn

Câu trả lời:


9

130 121 byte

-9 byte nhờ Ørjan Johansen

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

Hãy thử trực tuyến!

Điều này xác định các họ kiểu đóng để cộng (+)và nhân (*). Sau đó, một lớp loại (#)được định nghĩa sử dụng họ (*)kiểu cùng với một ràng buộc đẳng thức để chuyển đổi từ thế giới của các kiểu gia đình sang thế giới của kiểu chữ prologlass.


3
Nếu bạn trao đổi các phương trình, bạn có thể thay thế Zerobằng z.
Ørjan Johansen

1
@ ØrjanJohansen Xong. Tôi lưu 9 byte cho ai đó và 9 byte được lưu cho tôi.
Khoai tây44

Tôi không biết cách sử dụng các loại gia đình, nhưng có lẽ một chức năng như thế này nên bạn không cần xác định +là hữu ích?
Lynn

@Lynn mà kết thúc ra dài hơn. TIO
Khoai tây44

1
@WheatWizard Tôi mới nhận ra rằng mã tôi đã đăng trong bình luận vì nó xuất hiện lâu hơn về cơ bản là phiên bản đệ quy đuôi của câu trả lời của bạn.
Khoai tây44

6

139 byte

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

Hãy thử trực tuyến!

Xác định một toán tử loại *. Tương đương với chương trình Prolog:

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 và Hat Wizard đã lưu 9 byte mỗi cái. Cảm ơn!


Bạn không cần phải tính khai báo dữ liệu vào tổng byte của mình. Tôi sẽ làm cho điều này rõ ràng hơn trong câu hỏi khi tôi có cơ hội
Ad Hoc Garf Hunter

Ngoài ra tôi nghĩ rằng bạn có thể sử dụng một vị tướng fthay vì Succ.
Ad Hoc Garf Hunter

1
Bạn có thể lưu 9 byte bằng cách bỏ dấu hai chấm.
Khoai tây44

Tôi nghĩ Hat Wizard cũng đã cứu 9 chứ không phải 6. Có ba lần xuất hiện của Succ.
Khoai tây44

1

Phiên bản gia đình, 115 byte

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

Hãy thử trực tuyến!

Đây là sử dụng một gia đình kiểu kín như khoai tây44 . Ngoại trừ không có câu trả lời khác, tôi chỉ sử dụng 1 loại gia đình.

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

Điều này định nghĩa một toán tử trên ba loại. Nó chủ yếu thực hiện (a*b)+c. Bất cứ khi nào chúng tôi muốn thêm đối số tay phải của chúng tôi vào tổng số, chúng tôi thay vào đó đặt nó vào bộ tích lũy.

Điều này ngăn cản chúng ta cần phải xác định (+)tất cả. Về mặt kỹ thuật, bạn có thể sử dụng gia đình này để thực hiện bổ sung bằng cách thực hiện

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

Phiên bản lớp, 137 byte

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

Hãy thử trực tuyến!

Phiên bản lớp này mất một số nền tảng cho phiên bản gia đình, tuy nhiên nó vẫn ngắn hơn phiên bản lớp ngắn nhất ở đây. Nó sử dụng phương pháp tương tự như phiên bản gia đình của tôi.


Thật tuyệt, tôi thấy rằng gia đình kiểu của bạn đang thực hiện một cách toán học một * b + c. Là đề cập đến "phân chia" có nghĩa là "bổ sung"?
Khoai tây44

btw, bạn đang vi phạm thông số kỹ thuật của riêng bạn tại thời điểm này. "Thực hiện một lớp nhân hai số Peano" Những gì bạn hiện không phải là một lớp, Constraintmặc dù nó thực sự là loại . Vì vậy, bạn nên cập nhật thông số kỹ thuật hoặc hoàn nguyên về biểu mẫu sử dụng một lớp thay vì từ đồng nghĩa loại. Nếu tôi sử dụng từ đồng nghĩa loại, tôi có thể nhận được câu trả lời của mình xuống 96 byte, vì vậy nó sẽ tiết kiệm cho tôi thêm một byte so với bạn
Potato44

@ Potato44 Tôi đã có ấn tượng rằng một lớp học chỉ là một cái gì đó với một loại kết quả trong một chống chỉ định. Có lẽ đó là do sự thiếu rõ ràng trong câu hỏi. Tôi sẽ trở lại câu trả lời 115 của tôi sau đó.
Ad Hoc Garf Hunter
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.