Tại sao 'zip' lại bỏ qua cái đuôi lơ lửng của bộ sưu tập?


12

C # , Scala, Haskell, Lisp và Python có cùng một ziphành vi: nếu một bộ sưu tập dài hơn, đuôi sẽ âm thầm bị bỏ qua.

Nó cũng có thể là một ngoại lệ, nhưng tôi không nghe thấy bất kỳ ngôn ngữ nào sử dụng phương pháp này.

Điều này đánh đố tôi. Có ai biết lý do tại sao zipđược thiết kế theo cách đó? Tôi đoán đối với các ngôn ngữ mới, nó được thực hiện bởi vì các ngôn ngữ khác làm theo cách này. Nhưng lý do gốc rễ là gì?

Tôi đang hỏi ở đây câu hỏi thực tế, dựa trên lịch sử, không phải nếu ai đó thích nó, hoặc nếu đó là một cách tiếp cận tốt hay xấu.

Cập nhật : Nếu tôi được hỏi phải làm gì, tôi sẽ nói - ném một ngoại lệ, khá giống với việc lập chỉ mục một mảng (mặc dù các ngôn ngữ "cũ" đã làm tất cả các loại phép thuật, làm thế nào để xử lý các chỉ số giới hạn, UB, mở rộng mảng, Vân vân).


10
Nếu nó không bỏ qua cái đuôi mà một functor có, sử dụng các chuỗi vô hạn sẽ trở nên cồng kềnh hơn. Đặc biệt nếu nhận được độ dài của phạm vi không vô hạn là tốn kém / phức tạp / không thể.
Ded repeatator

2
Bạn dường như nghĩ rằng điều này là bất ngờ và kỳ lạ. Tôi thấy nó rõ ràng và, thực sự, không thể tránh khỏi. Điều gì bạn muốn xảy ra khi bạn nén các bộ sưu tập có độ dài không bằng nhau?
Kilian Foth

@KilianFoth, bị ném ngoại lệ.
greenoldman

@Ded repeatator, cái hay đấy. Với việc thả đuôi im lặng, bạn có thể tự nhiên thể hiện zipWithIndexviệc cung cấp trình tạo số tự nhiên. Bây giờ, các mảnh chỉ thiếu thông tin - là những gì là lý do? :-) (btw. vui lòng đăng lại nhận xét của bạn dưới dạng câu trả lời, cảm ơn bạn).
greenoldman

1
Python có itertools.izip_longest, có hiệu quả tự động hoàn thành các đầu vào với Nones. Tôi chọn nó qua zip thường xuyên khi tôi thực sự sử dụng zip; Tôi không thể nhớ những lý do đằng sau bất kỳ sự lựa chọn nào nữa. Python đã liệt kê () cho trường hợp của @ greenoldman, mà tôi thường sử dụng.
StarWeaver

Câu trả lời:


11

Nó hầu như luôn luôn là những gì bạn muốn, và khi không, bạn có thể tự làm.

Vấn đề chính là với ngữ nghĩa lười biếng, bạn không biết độ dài khi mới bắt đầu zip, vì vậy bạn không thể ném ngoại lệ khi bắt đầu. Trước tiên, bạn cần trả về tất cả các yếu tố phổ biến, sau đó đưa ra một ngoại lệ, điều này sẽ không hữu ích.

Đó cũng là một vấn đề phong cách. Các lập trình viên bắt buộc đã quen với việc kiểm tra thủ công các điều kiện biên ở khắp mọi nơi. Các lập trình viên chức năng thích các cấu trúc không thể thất bại theo thiết kế. Trường hợp ngoại lệ là cực kỳ hiếm. Nếu có một cách để một hàm trả về một mặc định hợp lý, các lập trình viên hàm sẽ lấy nó. Khả năng kết hợp là vua.


Tôi đang hỏi về lý do lịch sử, không phải những gì tôi có thể làm. Đoạn thứ hai - bạn đã sai, hãy xem cách ziphiện đang được thực hiện. Ném ngoại lệ chỉ đơn giản là thay đổi "dừng sản lượng" thành "ném". Đoạn thứ ba - trả về phần tử trống để vượt ra khỏi ranh giới không thể thất bại, nhưng tôi nghi ngờ bất kỳ nhà phát triển FP nào sẽ bỏ phiếu đó là một thiết kế tốt.
greenoldman

3
Đoạn thứ hai của tôi không áp dụng cho tất cả các triển khai, chỉ những người thực sự lười biếng. Nếu ziphai chuỗi vô hạn cùng nhau, bạn sẽ không biết kích thước khi bắt đầu. Trên đoạn thứ ba, tôi nói mặc định hợp lý . Trả lại sản phẩm nào trong trường hợp này sẽ không hợp lý, trong khi việc bỏ đuôi rõ ràng là như vậy.
Karl Bielefeldt

Ah, tôi thấy quan điểm của bạn cuối cùng - với ngoại lệ trong ngôn ngữ lười biếng, nó không phải là sự thay thế kỹ thuật, nó hoàn toàn thay đổi hành vi, bởi vì bạn cần ném một ngoại lệ ngay khi bắt đầu, trong khi bạn có thể bỏ qua đuôi bất cứ khi nào thuận tiện.
greenoldman 3/03/2015

3
+1 đây cũng là một câu trả lời tuyệt vời, "Các lập trình viên chức năng thích các cấu trúc không thể thất bại trong thiết kế", điều này nói lên một cách hùng hồn đâu là động lực lớn nhất đằng sau phần lớn các quyết định thiết kế mà các lập trình viên chức năng đưa ra. Các lập trình viên bắt buộc có một quy tắc mà họ thích là "Nói, đừng hỏi", FP đưa điều này đến mức thứ N bằng cách tập trung vào việc cho phép nói hướng dẫn liên tục mà không yêu cầu kiểm tra kết quả cho đến giây phút cuối cùng, vì vậy chúng tôi cố gắng đảm bảo các bước trung gian không thể thất bại, bởi vì Composability là vua. Nói rất hay.
Jimmy Hoffa

12

Bởi vì không có cách rõ ràng để hoàn thành cái đuôi. Bất kỳ lựa chọn về cách làm nó sẽ dẫn đến một cái đuôi không rõ ràng.

Mẹo nhỏ là kéo dài một cách rõ ràng danh sách ngắn nhất của bạn để phù hợp với độ dài dài nhất với các giá trị bạn mong đợi.

Nếu zip đã làm điều đó cho bạn, bạn không thể biết giá trị nào được điền vào bằng trực giác. Nó đã chu kỳ danh sách? Nó đã lặp lại một giá trị mempty? Giá trị mempty cho loại của bạn là gì?

Không có hàm ý gì về việc zip mà người ta có thể sử dụng để giao tiếp theo cách kéo dài đuôi, vì vậy điều hợp lý duy nhất cần làm là làm việc với các giá trị có sẵn thay vì làm cho người tiêu dùng của bạn không mong đợi.


Cũng nên nhớ rằng bạn đang đề cập đến một chức năng nổi tiếng rất cụ thể với ngữ nghĩa nổi tiếng cụ thể. Nhưng điều đó không có nghĩa là bạn không thể tạo ra một chức năng tương tự nhưng hơi khác nhau . Chỉ vì có một chức năng chung xkhông có nghĩa là bạn không thể quyết định cho mục đích đã định mà bạn muốn làm xy.

Mặc dù hãy nhớ lý do này và nhiều hàm kiểu FP phổ biến khác là phổ biến, bởi vì chúng đơn giản và khái quát để bạn có thể điều chỉnh mã của mình để sử dụng chúng và có được hành vi bạn muốn. Chẳng hạn, trong C # bạn chỉ có thể

IEnumerable<Tuple<T, U>> ZipDefaults(IEnumerable<T> first, IEnumerable<U> second)
{
    return first.Count() < second.Count()
        ? first.Concat(Enumerable.Repeat(default(T), second.Count() - first.Count())).Zip(second)
        : first.Zip(second.Concat(Enumerable.Repeat(default(U), first.Count() - second.count())))
}

Hoặc những điều đơn giản khác. Phương pháp tiếp cận FP làm cho việc sửa đổi trở nên dễ dàng bởi vì bạn có thể sử dụng lại các phần cũng như việc triển khai quá nhỏ như trên mà việc tạo các phiên bản sửa đổi của riêng bạn là cực kỳ đơn giản.


Ok, nhưng chỉ khi bạn buộc các bộ sưu tập làm một cái gì đó để khớp với cái khác - so sánh nó với việc lập chỉ mục bộ sưu tập (mảng). Bạn có thể bắt đầu suy nghĩ tôi có nên mở rộng và mảng nếu tôi có chỉ số ngoài giới hạn không? Hoặc có thể âm thầm bỏ qua yêu cầu. Nhưng trong một thời gian, có một khái niệm phổ biến về ném ngoại lệ. Tương tự ở đây - nếu bạn không có bộ sưu tập phù hợp, hãy ném một ngoại lệ. Tại sao phương pháp này không được thực hiện?
greenoldman

2
zipcó thể điền vào null, thường là một giải pháp trực quan. Hãy xem xét các loại zip :: [a] -> [b] -> [(Maybe a, Maybe b)]. Cấp, loại kết quả là một chút ^ H ^ H khá không thực tế, nhưng nó sẽ cho phép dễ dàng thực hiện bất kỳ hành vi nào khác (cắt ngắn, ngoại lệ) trên đầu trang.
amon

1
@amon: Điều đó không trực quan chút nào, nó thật ngớ ngẩn. Nó sẽ chỉ yêu cầu kiểm tra null mọi đối số.
DeadMG

4
@amon không phải loại nào cũng có null, đó là ý của tôi mempty, các đối tượng có null để lấp đầy khoảng trống, nhưng bạn có muốn nó phải đưa ra một thứ như vậy cho int và các loại khác không? Chắc chắn, C # có default(T)nhưng không phải tất cả các ngôn ngữ đều làm được, và ngay cả đối với C # đó có phải là hành vi thực sự rõ ràng ? Tôi không nghĩ vậy
Jimmy Hoffa

1
@amon Có lẽ sẽ hữu ích hơn khi trả lại phần chưa biết trong danh sách dài hơn. Bạn có thể sử dụng điều đó để kiểm tra xem chúng có dài bằng nhau hay không nếu bạn cần, và vẫn có thể nén lại hoặc làm gì đó với cái đuôi không được phát hiện mà không cần duyệt lại danh sách.
Doval
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.