2012-11-17 39 views
21

tôi có chức năng sau đây trong HaskellLàm thế nào để viết lại một hàm Haskell của hai luận điểm miễn phí theo phong cách

agreeLen :: (Eq a) => [a] -> [a] -> Int 
agreeLen x y = length $ takeWhile (\(a,b) -> a == b) (zip x y) 

Tôi đang cố gắng tìm hiểu làm thế nào để viết 'thành ngữ' Haskell, mà dường như thích sử dụng .$ thay vì dấu ngoặc đơn, đồng thời cũng thích mã pointfree nếu có thể. Tôi dường như không thể loại bỏ việc đề cập đến số xy một cách rõ ràng. Bất kỳ ý tưởng?

Tôi nghĩ rằng tôi muốn có cùng một vấn đề với việc chấm dứt bất kỳ chức năng nào của hai đối số.

BTW, đây chỉ là việc theo đuổi viết mã tốt; không một số "sử dụng bất cứ điều gì nó cần để làm cho nó pointfree" bài tập về nhà bài tập.

Cảm ơn.


(Đã thêm nhận xét) Cảm ơn câu trả lời. Bạn đã thuyết phục tôi chức năng này không được hưởng lợi từ pointfree. Và bạn cũng đã cho tôi một số ví dụ tuyệt vời để thực hành các biểu thức chuyển đổi. Nó vẫn còn khó khăn đối với tôi, và họ dường như là điều cần thiết để Haskell như con trỏ là để C.

+2

Tôi đã tìm thấy hlint (http://community.haskell.org/~ndm/darcs/hlint/hlint.htm) rất có giá trị trong việc giúp tôi tìm hiểu các cách khác để viết các biểu thức, kể cả sử dụng. và $. – mhwombat

Trả lời

41

và cũng thích sử dụng mã điểm ngoài nếu có thể.

Không phải "nếu có thể", nhưng "nơi nó cải thiện khả năng đọc (hoặc có các lợi ích rõ ràng khác)".

Để chỉ-miễn phí của bạn

agreeLen x y = length $ takeWhile (\(a,b) -> a == b) (zip x y) 

Bước đầu tiên sẽ được di chuyển ($) phải, và thay thế một trong những bạn có với một (.):

agreeLen x y = length . takeWhile (\(a,b) -> a == b) $ zip x y 

Bây giờ, bạn có thể di chuyển nó hơn nữa ngay:

agreeLen x y = length . takeWhile (uncurry (==)) . zip x $ y 

và ở đó bạn có thể cắt ngay lập tức tắt một đối số,

agreeLen x = length . takeWhile (uncurry (==)) . zip x 

Sau đó, bạn có thể viết lại rằng như một ứng dụng tiền tố của các nhà điều hành thành phần,

agreeLen x = (.) (length . takeWhile (uncurry (==))) (zip x) 

và bạn có thể viết

f (gx)

như

f . g $ x 

thường, ở đây với

f = (.) (length . takeWhile (uncurry (==))) 

g = zip, cho

agreeLen x = ((.) (length . takeWhile (uncurry (==)))) . zip $ x 

từ đó lập luận x có thể dễ dàng gỡ bỏ. Sau đó, bạn có thể chuyển đổi các ứng dụng tiền tố của (.) thành một phần và nhận được

agreeLen = ((length . takeWhile (uncurry (==))) .) . zip 

Nhưng, đó là ít có thể đọc được so với bản gốc, vì vậy tôi không khuyên bạn nên làm điều đó trừ thực hành việc chuyển đổi biểu thức thành điểm miễn phí Phong cách.

+9

Điều thực sự đáng chú ý là một chàng trai tên là Schönfinkel đã phát minh ra kỹ thuật này vào năm 1920. –

11

Bạn cũng có thể sử dụng:

agreeLen :: (Eq a) => [a] -> [a] -> Int 
agreeLen x y = length $ takeWhile id $ zipWith (==) x y 

Idiomatic Haskell là bất cứ điều gì là dễ dàng hơn để đọc, không nhất thiết phải là những gì hầu hết không có điểm.

+0

Sử dụng zipVới trông đẹp hơn rất nhiều. Nhưng phiên bản "dễ đọc" của tôi rất khác so với hầu hết các nhà văn của Haskell, vì vậy tôi đang cố gắng di chuyển phiên bản của mình gần hơn với họ. – JonathanZ

+3

@Jonathan Vâng, ngay cả cộng đồng cũng có ý kiến ​​khác nhau về phong cách, nhưng tôi có thể nói với bạn rằng quy ước của tôi là sử dụng '.' bất cứ khi nào bạn có chuỗi chức năng của một đối số, và nếu bạn sử dụng hai hoặc nhiều đối số chúng rõ ràng. Đối với '$', bạn sử dụng nó chủ yếu để loại bỏ các dấu ngoặc đơn ở cuối khối 'do', chẳng hạn như với' foo $ do ... ', hoặc để dọn dẹp các dấu ngoặc đơn lồng nhau. –