2015-04-15 15 views

Trả lời

16

Theo định nghĩa của (.):

flip const 1 $ ((const flip 3) const 4) 5 

Theo định nghĩa của const:

= flip const 1 $ flip const 4 5 

Theo định nghĩa của flip:

= flip const 1 $ const 5 4 

Theo định nghĩa của const:

= flip const 1 5 

Theo định nghĩa của flip:

= const 5 1 

Đó là 5.

(Là một cái nhìn sâu sắc bonus chút, bạn có thể tìm hiểu tại sao flip const y chỉ id cho tất cả y là gì? Điều này làm giảm biểu hiện của bạn để (id . id) 5.)

+0

Tôi gần như hiểu nhưng bạn có thể vui lòng xây dựng ít hơn về cách ((const lật 3) const 4) 5 trở thành lật const 4 5. –

+0

'const xy' là' x', vì vậy 'const flip 3' là' flip'. – Lynn

+0

(Và sau đó: bạn có biết làm thế nào '(fx) y' và' fxy' luôn luôn là chính xác điều tương tự trong Haskell, vì currying? Nếu chúng ta áp dụng thay thế tôi vừa đề cập, chúng ta nhận được '(flip const 4) 5', chỉ là 'flip const 4 5'.) – Lynn

2

Mỗi khi bạn thấy một số “thông minh” chức năng gọi trong Haskell , luôn kiểm tra các loại của nó, sau đó thử chơi với nó trong GHCi. Cách duy nhất để tìm hiểu Haskell thực sự là thử nó, và bạn không thể làm điều đó chỉ bằng cách đọc câu trả lời StackOverflow hoặc hướng dẫn trực tuyến, bạn phải đặt biểu thức vào GHCi, thất bại thảm hại, cố gắng tìm ra lỗi cho bản thân bạn, cố gắng thay thế các giá trị khác nhau ở những nơi khác nhau để làm cho lỗi biến mất, cố gắng phân chia vấn đề thành các phần và hiểu chúng một cách riêng biệt, v.v.

Không thể mã Haskell mà không hiểu hệ thống kiểu của nó (mà, tôi hứa, không khó). Nếu bạn không hiểu những gì, ví dụ, một "biến loại" có nghĩa là, đọc chương trên Types and Typeclasses, và chắc chắn rằng bạn siêng năng thử tất cả các biểu thức trong GHCi.

biểu hiện ban đầu của bạn là:

(flip const 1 . const flip 3 const 4) 5 

(.)function composition, và nếu typechecks biểu thức (tức là nó không ném một loại lỗi thời gian biên dịch), sau đó flip const 1const flip 3 const 4 đã nhận được chức năng.

Tập thể dục: Bây giờ mở GHCi và nhập dữ liệu sau từng người một và kiểm tra các loại (:t GHCi lệnh là để kiểm tra một loại của một số giá trị):

:t const 
:t flip 

Trước hết , hãy nhớ sự thật này về hệ thống kiểu của Haskell.Nếu có biến kiểu, chẳng hạn như a, trong khai báo kiểu, thì hàm phải chấp nhận bất kỳ loại nào. Nếu có một biến kiểu được bao quanh bởi một kiểu chữ, ví dụ: f :: Num a => a -> a, sau đó hàm phải chấp nhận bất kỳ loại nào, đó là trường hợp của typeclass Num (ví dụ: bất kỳ số nào). Bạn không thể có hàm trả về a, nhưng trong cơ thể bạn trả về một hàm String.

Thứ hai, nếu có nhiều hơn 1 loại biến trong khai báo loại, loại của chúng luôn giống nhau. Vì vậy, nếu bạn có một hàm có loại f :: a -> a và bạn đã cho nó một số Int, hàm này phải trả lại Int. Bởi vì Haskell không có kiểm tra kiểu thời gian chạy, bạn không thể giả định bất cứ điều gì về các loại, nếu bạn chấp nhận một biến kiểu.

Hai sự kiện này hạn chế chức năng có thể làm, nếu có một loại nhất định. Do đó:

  • f :: a -> a luôn trả về đối số; nó không thể lý luận về đối số, bởi vì nó không biết loại của nó, nó không thể áp dụng bất kỳ hàm nào với các kiểu cụ thể hơn, vì vậy nó phải trả về đối số
  • f :: (a, b) -> afst và không có gì khác, không biết gì về các yếu tố đầu tiên của một cặp, vì vậy nó phải chỉ trả lại
  • f :: (a, b) -> bsnd, với cùng lý do
  • f :: a -> b -> a mất 2 đối số và trả về đầu tiên
  • f :: b -> a -> a mất 2 lập luận và trả về số thứ hai

Hạn chế này là một điều tốt, bởi vì chỉ bằng cách nhìn vào loại bạn có thể đoán rất nhiều về hàm. Một số loại chỉ là không thể, ví dụ: f :: a -> b, không có chức năng nào thuộc loại đó (vì điều gì sẽ b là sao?).

Hãy nhớ rằng, -> trong khai báo kiểu là right-associative, vì vậy:

f :: (a -> b -> c) -> b -> a -> c 
f :: (a -> b -> c) -> b -> (a -> c) 
f :: (a -> b -> c) -> (b -> a -> c) 
f :: (a -> b -> c) -> (b -> (a -> c)) 
f :: (a -> (b -> c)) -> (b -> (a -> c)) 

đều tương đương. Vì vậy, bạn có thể xử lý một hàm thuộc loại f :: a -> b -> c khi lấy 2 đối số và trả về một giá trị hoặc bạn có thể coi nó là tham số 1 và trả về một hàm của 1 đối số.

“Chờ đã,” bạn có thể nói, “bạn nói một chức năng của loại a -> b là không thể, thì làm sao có thể f là kiểu (a -> b -> c) -> b -> (a -> c), đó là, trở về một chức năng của loại a -> c?” Vâng, f thực sự không bao giờ trả về một chức năng của loại a -> c, bởi vì bạn không bao giờ vượt qua một chức năng loại a -> b -> c cho nó. Thay vào đó, bạn có thể chuyển một hàm thuộc loại Int -> a -> [a] và nhận hàm số loại a -> (Int -> [a]).

Sau đó, bạn chuyển một giá trị khác, giả sử loại String, với hàm mới này. Bạn nhận được một hàm số loại Int -> [String]. Bây giờ Int -> [String] không phải là một chức năng không thể, phải không?

Về mặt kỹ thuật, bạn có thể trả về một chức năng loại a -> b, được gọi là undefined. undefined là giá trị của loại a, tức là, nó có một loại bất kỳ loại nào. Nhưng đó là một chủ đề cho một thời điểm khác.

Tập thể dục: Vì vậy, chúng ta hãy tiếp tục chơi với các loại trong GHCi:

:t const 
:t flip 
:t const flip 
:t flip const 

bạn đã học được gì từ các loại? Đối số nào các chức năng này sẽ bỏ qua? Giá trị (hoặc chức năng) nào sẽ trở lại?

Tập thể dục: Nếu bạn vẫn không hiểu, nơi tôi đang hướng, cố gắng nhập các thành GHCi theo thứ tự đó, để xem làm thế nào một phần ứng dụng thay đổi các loại:

:t flip 
:t flip const 
:t flip const 1 
:t const 
:t const flip 
:t const flip 3 
:t const flip 3 const 
:t const flip 3 const 4 
:t id 
Các vấn đề liên quan