2012-05-01 47 views
14

Tôi đang đọc qua số Gentle Introduction và tự hỏi tại sao trong danh sách hiểu với hai trình tạo, trình tạo đúng bên phải được lặp lại "nhanh nhất" (tức là biên dịch đến vòng lặp trong cùng, tôi đoán). Quan sát đầu ra GHCi sau:Tại sao tính năng hiểu danh sách Haskell với nhiều máy phát điện xử lý máy phát điện ngoài cùng bên phải là vòng lặp chặt chẽ nhất?

*Main> concat [[(x,y) | x <- [0..2]] | y <- [0..2]] 
[(0,0),(1,0),(2,0),(0,1),(1,1),(2,1),(0,2),(1,2),(2,2)] 
*Main> [(x,y) | x <- [0..2], y <- [0..2]] 
[(0,0),(0,1),(0,2),(1,0),(1,1),(1,2),(2,0),(2,1),(2,2)] 

Nếu máy phát điện tận cùng bên trái được lặp nhanh nhất, hai biểu thức trên sẽ có giá trị như nhau, mà tôi nghĩ rằng làm cho việc lựa chọn Công ước này tự nhiên hơn bằng cách nào đó.

Vì vậy, có ai biết tại sao quy ước ngược lại đã được chọn không? Tôi nhận thấy Python có cùng quy ước như Haskell (thậm chí có thể mượn nó từ Haskell?), Và trong thế giới Python the word có vẻ là thứ tự đã được chọn "bởi vì đó là thứ tự bạn muốn viết vòng lặp", nhưng tôi thu thập suy nghĩ đó về vòng lặp không phải là chính xác những gì hầu hết các lập trình viên Haskell làm ...

Suy nghĩ?


Từ nhận xét của tôi về câu trả lời Louis Wasserman của dưới đây:

Tôi đoán đây thứ tự tương ứng với một sự giải thích bắt buộc giữa các đồng chí hiểu được coi là tự nhiên hơn có nó tương ứng với làm tổ trong danh sách. Vì vậy, trong bản chất giải thích Haskell cho điều này là giống như lời giải thích Python tôi liên kết trong câu hỏi, sau khi tất cả, có vẻ như.

+0

Làm thế nào điều này sẽ * tự nhiên hơn *? Bạn cũng muốn 11, 21, 31, 41 thay vì 11, 12, 13, 14? – Ingo

+0

Tôi đoán sử dụng '(y, x)' làm biểu thức nguyên mẫu - hoặc đặt trình tạo y ở bên trái của trình tạo x - sẽ có ý nghĩa hơn nếu máy phát bên trái nhất là vòng lặp chặt chẽ nhất. Sau đó, nó sẽ là dòng thứ hai trong sản lượng GHCi của tôi trông lạ với bạn (11, 21, 31, 41), chứ không phải là cái đầu tiên, nhưng chúng vẫn khác nhau, đó là quan điểm của tôi. – kini

+0

Xin lỗi, tôi đoán tôi nên giải quyết bạn khi trả lời bạn, @Ingo. (Loại mới để Stack tràn.) – kini

Trả lời

21

Vì vậy, mọi thứ đều theo một cách lành mạnh.

[(x, y) | x <- [1..10], y <- [1..x]] 

làm cho tinh thần - x là trong phạm vi cho sự hiểu biết về y - nhưng

[(x, y) | y <- [1..x], x <- [1..10]] 

làm cho hơi ít ý nghĩa.

Thêm vào đó, cách này đó là phù hợp với cú pháp do đơn nguyên:

do x <- [1..10] 
    y <- [1..x] 
    return (x, y) 
+3

Có thể là một ý tưởng tốt để hiển thị cách mã giống nhau sẽ sử dụng cú pháp 'do' monad, cho người không bắt đầu. – dflemstr

+0

Hmm. Là một người mới bắt đầu, tôi thực sự không thấy cách phạm vi là bất kỳ ít tùy ý hơn so với thứ tự của máy phát điện. Một lần nữa sử dụng ví dụ lồng nhau của tôi, '[[(x, y) | x <- [1..10]] | y <- [1..x]] 'không có ý nghĩa, trong khi' [[(x, y) | y <- [1..x]] | x <- [1..10]] 'có ý nghĩa. – kini

+2

Làm tổ (và có nghĩa là) hoàn toàn khác với cú pháp hiểu được chuỗi. Trong mọi trường hợp, ký hiệu 'do' có một thứ tự rõ ràng mà danh sách hiểu được đồng ý với. –

0

Trên thực tế Python sử dụng cấu trúc phạm vi tương tự như Haskell cho comprehensions danh sách.

Hãy so sánh Haskell của bạn:

*Main> concat [[(x,y) | x <- [0..2]] | y <- [0..2]] 
[(0,0),(1,0),(2,0),(0,1),(1,1),(2,1),(0,2),(1,2),(2,2)] 
*Main> [(x,y) | x <- [0..2], y <- [0..2]] 
[(0,0),(0,1),(0,2),(1,0),(1,1),(1,2),(2,0),(2,1),(2,2)] 

với Python này:

>>> from itertools import chain 
>>> list(chain(*[[(x,y) for x in range(3)] for y in range(3)])) 
[(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1), (0, 2), (1, 2), (2, 2)] 
>>> [(x,y) for x in range(3) for y in range(3)] 
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)] 
>>> 
+0

Rất tiếc, bạn đã biết. Dù sao đi nữa, tôi sẽ để cái này ở đây để so sánh. –

+0

Er, vâng, đó là những gì tôi có ý nghĩa :) Tôi sẽ chỉnh sửa câu hỏi của tôi cho rõ ràng. – kini

6

Nó có thể có ý nghĩa hơn nếu bạn mở rộng danh sách hiểu biết đầu tiên vào do ký hiệu và sau đó vào với phím tắt monadic.Hãy nói rằng chúng tôi muốn viết một sự hiểu biết mà chúng ta xem lại tên được đã bị ràng buộc:

[ (x,y) | x <- [1,2,3], y <- [x+1,x+2] ] 

này mở rộng để

do x <- [1,2,3] 
    y <- [x+1,x+2] 
    return (x,y) 

mà mở rộng để

[1,2,3] >>= \x -> 
[x+1,x+2] >>= \y -> 
return (x,y) 

mà làm cho nó rõ ràng rằng x nằm trong phạm vi chính xác khi cần.

Nếu việc mở rộng thành do ký hiệu đã xảy ra từ phải sang trái thay vì từ trái sang phải, sau đó biểu hiện ban đầu của chúng tôi sẽ mở rộng sang

[x+1,x+2] >>= \y -> 
[1,2,3] >>= \x -> 
return (x,y) 

đó rõ ràng là vô nghĩa - nó đề cập đến giá trị của x trong phạm vi mà x chưa bị ràng buộc. Vì vậy, chúng tôi phải viết hiểu biết ban đầu của chúng tôi như

[ (x,y) | y <- [x+1,x+2], x <- [1,2,3] ] 

để có được những kết quả chúng tôi muốn, mà có vẻ không tự nhiên - vào thời điểm đó quét mắt qua các cụm từ y <- [x+1,x+2] bạn không thực sự biết x là gì. Bạn sẽ phải đọc hiểu ngược để tìm hiểu.

Vì vậy, nó không phải cần là trường hợp liên kết bên phải nhất được bỏ vào "vòng lặp bên trong" nhưng nó có ý nghĩa khi bạn xem xét rằng con người sẽ phải đọc mã kết quả .

+0

Có, bạn cũng có những điểm tương tự với câu trả lời của Louis Wasserman.Nhưng nếu lý luận thực sự phù hợp với thứ tự sắp xếp từ trái sang phải để phù hợp với (nói tiếng Anh) con người đọc từ trái sang phải, thì tại sao biểu thức ('(x, y)') ở bên trái của định lượng các biến của nó? Thực sự, tại sao không chỉ làm theo thứ tự đơn thuần của hướng dẫn tất cả các cách, do đó, [1,2,3] >> = \ x -> [x + 1, x + 2] >> = \ y -> trả lại (x, y) 'được sugared như, nói,' [x <- [1,2,3], y <- [x + 1, x + 2] | (x, y)] '? – kini

+0

Nói cách khác, đúng, khi bạn quét 'y <- [x + 1, x + 2]', bạn không thực sự biết 'x' là gì, nhưng khi bạn quét' (x, y) ' , bạn không biết một trong hai 'x' hoặc' y' là gì. – kini

+2

Đó là một điểm hợp lệ. Tôi nghi ngờ lý do để viết nó theo cách này là nó song song với toán học [set-builder notation] (http://en.wikipedia.org/wiki/Set-builder_notation#Parallels_in_programming_languages) (hãy nhớ rằng Haskell có nhiều ảnh hưởng từ toán học). Trong trường hợp nó giúp bạn ra ngoài - tôi luôn đọc thanh dọc '|' là "ở đâu" (tôi đã từng đọc nó là "như vậy" trong những ngày tôi tự coi mình là một nhà toán học hơn là một lập trình viên ...) –