5

Tôi là người mới bắt đầu ở Haskell và tôi đang cố gắng nắm bắt nó.Ứng dụng một phần chức năng và currying, cách tạo mã tốt hơn thay vì nhiều bản đồ?

Tôi gặp vấn đề sau đây:

Tôi đã một chức năng mà được 5 thông số, cho phép nói

f x y w z a = x - y - w - z - a 

Và tôi muốn áp dụng nó trong khi chỉ có thay đổi biến x1-10 trong khi y, w, za sẽ luôn giống nhau. Việc thực hiện tôi đạt được là sau nhưng tôi nghĩ rằng phải có một cách tốt hơn.

Hãy nói rằng tôi muốn sử dụng:

x from 1 to 10 
y = 1 
w = 2 
z = 3 
a = 4 

Theo đó đến nay tôi quản lý để áp dụng các chức năng như sau:

map ($ 4) $ map ($ 3) $ map ($ 2) $ map ($ 1) (map f [1..10]) 

tôi nghĩ rằng phải có một cách tốt hơn để áp dụng rất nhiều thiếu tham số cho các hàm được áp dụng một phần mà không phải sử dụng quá nhiều bản đồ.

Trả lời

4

Giả sử bạn không quan tâm đến các biến, bạn chỉ cần xác định hàm mới cần x và gọi f. Nếu bạn không có định nghĩa chức năng ở đó (thông thường bạn có thể sử dụng let hoặc where), bạn có thể sử dụng lambda thay thế.

f' x = f x 1 2 3 4 

Hoặc với một lambda

\x -> f x 1 2 3 4 
4

currying sẽ không làm bạn bất kỳ tốt ở đây, bởi vì đối số bạn muốn thay đổi (liệt kê) không phải là người cuối cùng. Thay vào đó, hãy thử một cái gì đó như thế này.

map (\x -> f x 1 2 3 4) [1..10] 
7

Những người khác đã thể hiện những cách bạn có thể làm, nhưng tôi nghĩ hữu ích khi chỉ cách chuyển đổi phiên bản của bạn thành một thứ gì đó đẹp hơn một chút. Bạn đã viết

map ($ 4) $ map ($ 3) $ map ($ 2) $ map ($ 1) (map f [1..10]) 

map tuân theo hai bộ luật cơ bản:

  1. map id = id. Tức là, nếu bạn ánh xạ chức năng nhận dạng trên bất kỳ danh sách nào, bạn sẽ quay lại cùng một danh sách.
  2. Đối với bất kỳ fg, map f . map g = map (f . g). Tức là, ánh xạ trên một danh sách với một hàm và sau đó một hàm khác giống như ánh xạ trên nó với thành phần của hai hàm đó.

Luật thứ hai map là luật chúng tôi muốn áp dụng tại đây.

map ($ 4) $ map ($ 3) $ map ($ 2) $ map ($ 1) (map f [1..10]) 
= 
map ($ 4) . map ($ 3) . map ($ 2) . map ($ 1) . map f $ [1..10] 
= 
map (($ 4) . ($ 3) . ($ 2) . ($ 1) . f) [1..10] 

($ a) . ($ b) làm gì? \x -> ($ a) $ ($ b) x = \x -> ($ a) $ x b = \x -> x b a. Điều gì về ($ a) . ($ b) . ($ c)? Đó là (\x -> x b a) . ($ c) = \y -> (\x -> x b a) $ ($ c) y = \y -> y c b a. Mẫu bây giờ phải rõ ràng: ($ a) . ($ b) ... ($ y) = \z -> z y x ... c b a.Vì vậy, cuối cùng, chúng tôi nhận

map ((\z -> z 1 2 3 4) . f) [1..10] 
= 
map (\w -> (\z -> z 1 2 3 4) (f w)) [1..10] 
= 
map (\w -> f w 1 2 3 4) [1..10] 
= 
map (\x -> ($ 4) $ ($ 3) $ ($ 2) $ ($ 1) $ f x) [1..10] 
5

Ngoài những gì các câu trả lời khác nói, nó có thể là một ý tưởng tốt để sắp xếp lại các thông số của chức năng của bạn, đặc biệt là x thường là tham số mà bạn thay đổi theo như thế:

f y w z a x = x - y - w - z - a 

Nếu bạn thực hiện nó để các tham số x đến cuối cùng, bạn chỉ có thể viết

map (f 1 2 3 4) [1..10] 

này sẽ không làm việc trong mọi hoàn cảnh o Tất nhiên, nhưng rất tốt để xem những thông số nào có nhiều khả năng thay đổi qua một loạt các cuộc gọi và đặt chúng vào cuối danh sách đối số và các tham số có xu hướng giữ nguyên theo hướng bắt đầu. Khi bạn làm điều này, currying và một phần ứng dụng thường sẽ giúp bạn nhiều hơn họ sẽ khác.

14

Tất cả đề xuất cho đến nay đều tốt. Đây là một cái khác, có vẻ hơi kỳ lạ lúc đầu, nhưng hóa ra lại khá tiện dụng trong nhiều tình huống khác.

Một số toán tử tạo hình dạng, chẳng hạn như [], là toán tử ánh xạ một loại thành phần, ví dụ: Int vào loại danh sách của các thành phần đó, [Int], có thuộc tính là Applicative. Đối với danh sách, điều đó có nghĩa là có một số cách, được biểu thị bởi nhà điều hành, <*>, được phát âm là "áp dụng", để biến danh sách của các hàm và liệt kê đối số vào danh sách kết quả.

(<*>) :: [s -> t] -> [s] -> [t] -- one instance of the general type of <*> 

chứ không phải là ứng dụng thông thường của bạn, đưa ra bởi một không gian trống, hoặc một $

($) :: (s -> t) -> s -> t 

Kết quả cuối cùng là chúng ta có thể làm lập trình chức năng bình thường với danh sách những thứ thay vì điều: đôi khi chúng ta gọi là nó "lập trình trong danh sách thành ngữ". Các chỉ thành phần khác là, để đối phó với tình hình khi một số linh kiện của chúng tôi là những thứ cá nhân, chúng ta cần thêm một tiện ích

pure :: x -> [x] -- again, one instance of the general scheme 

mà kết thúc tốt đẹp một điều lên như một danh sách, để tương thích với <*>. Đó là pure di chuyển một giá trị thông thường vào thành ngữ ứng dụng.

Đối với danh sách, pure chỉ cần tạo một danh sách đơn và <*> tạo kết quả của mỗi ứng dụng theo cặp của một trong các hàm đối với một trong các đối số. Đặc biệt

pure f <*> [1..10] :: [Int -> Int -> Int -> Int -> Int] 

là danh sách các chức năng (giống như map f [1..10]) mà có thể được sử dụng với <*> một lần nữa. Phần còn lại của đối số của bạn cho f không phải là danh sách, vì vậy bạn cần phải pure chúng.

pure f <*> [1..10] <*> pure 1 <*> pure 2 <*> pure 3 <*> pure 4 

Đối với danh sách, điều này mang lại

[f] <*> [1..10] <*> [1] <*> [2] <*> [3] <*> [4] 

tức là danh sách các cách để làm cho một ứng dụng từ f, một trong những [1..10], 1, 2, 3 và 4.

Việc mở pure f <*> s là quá phổ biến, nó được viết tắt f <$> s, vì vậy

f <$> [1..10] <*> [1] <*> [2] <*> [3] <*> [4] 

là những gì thường sẽ được ghi. Nếu bạn có thể lọc ra tiếng ồn <$>, pure<*>, nó trông giống như ứng dụng bạn đã nghĩ đến. Dấu chấm câu bổ sung chỉ cần thiết vì Haskell không thể biết sự khác biệt giữa tính toán danh sách của một loạt các hàm hoặc đối số và tính toán không liệt kê những gì được dự định là một giá trị duy nhất nhưng lại là danh sách. Ít nhất, tuy nhiên, các thành phần theo thứ tự bạn bắt đầu, vì vậy bạn thấy dễ dàng hơn những gì đang xảy ra.

Esoterica. (1) trong tôi (không phải là rất) private dialect của Haskell, ở trên sẽ được

(|f [1..10] (|1|) (|2|) (|3|) (|4|)|) 

nơi mỗi ngữ khung, (|f a1 a2 ... an|) đại diện cho ứng dụng của một hàm thuần bằng không hoặc nhiều đối số đó sống ở thành ngữ . Đó chỉ là một cách để viết

pure f <*> a1 <*> a2 ... <*> an 

Idris có dấu ngoặc đơn, nhưng Haskell chưa thêm chúng. Chưa.

(2) Trong ngôn ngữ có hiệu ứng đại số, thành ngữ tính toán không xác định không giống với kiểu gõ dữ liệu, mặc dù bạn có thể dễ dàng chuyển đổi giữa hai danh sách. Chương trình trở thành

f (range 1 10) 2 3 4 

trong đó phạm vi không xác định được chọn giá trị giữa giới hạn dưới và giới hạn cho trước. Do đó, không xác định được coi là một hiệu ứng phụ cục bộ, không phải là cấu trúc dữ liệu, cho phép hoạt động thất bại và lựa chọn. Bạn có thể bọc các tính toán không xác định trong một trình xử lý mang lại ý nghĩa cho các hoạt động đó và một trình xử lý như vậy có thể tạo danh sách tất cả các giải pháp. Đó là để nói, các ký hiệu bổ sung để giải thích những gì đang xảy ra được đẩy đến ranh giới, chứ không phải là rải qua toàn bộ nội thất, giống như những người <*>pure.

Quản lý ranh giới của sự vật thay vì nội thất của chúng là một trong số ít ý tưởng hay mà loài của chúng tôi đã có được. Nhưng ít nhất chúng ta có thể có nó hơn và hơn nữa. Đó là lý do tại sao chúng tôi trang trại thay vì săn bắn. Đó là lý do tại sao chúng tôi thích kiểm tra loại tĩnh để kiểm tra thẻ động. Và cứ thế ...

+3

một trong những hướng dẫn ứng dụng tốt nhất mà tôi từng thấy! –

+0

Không thực sự hiểu được tính không xác định phù hợp với các ứng dụng như thế nào – pedrofurla

+1

Tôi chưa hiểu những dấu ngoặc đơn nào khác ngoài nhiều cú pháp để học. Bạn dường như cảm thấy khá khác nhau về họ. Bạn có thể giải thích? – dfeuer

1

Các giải pháp chung trong tình huống này là một lambda:

\x -> f x 1 2 3 4 

Tuy nhiên, nếu bạn nhìn thấy mình làm điều này rất thường xuyên, với lập luận tương tự, nó sẽ làm cho tinh thần để di chuyển lập luận cho rằng là Lập luận cuối cùng thay vì:

\x -> f 1 2 3 4 x 

trong trường hợp currying áp dụng hoàn toàn tốt và bạn chỉ có thể thay thế các biểu thức trên với

f 1 2 3 4 

do đó bạn có thể viết:

map (f 1 2 3 4) [1..10] 
+1

Xin lỗi, nhưng '($ 1 2 3 4)' không * không * hoạt động. '$' chỉ xử lý một đối số. –

+0

oh oops, hiển nhiên là ... –

+0

'for' chỉ hoạt động đối với các tác vụ ứng dụng. Bạn sẽ cần phải gắn 'Identity' và 'runIdentity' và tất cả những điều vô nghĩa đó. – dfeuer

Các vấn đề liên quan