-
Notifications
You must be signed in to change notification settings - Fork 3
Add zip, zip-with and zip-all functions #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,10 @@ Overview | |
|
||
The sequence-utilities module implements some useful methods on sequences. | ||
|
||
- :func:`zip` | ||
- :func:`zip-all` | ||
- :func:`zip-with` | ||
|
||
|
||
Reference | ||
--------- | ||
|
@@ -363,3 +367,148 @@ far. https://github.com/dylan-lang/collection-extensions/issues/2 | |
:parameter list: An instance of :drm:`<list>`. | ||
:parameter elt: An instance of :drm:`<object>`. | ||
:value new-list: An instance of :drm:`<list>`. | ||
|
||
zip | ||
^^^ | ||
|
||
.. function:: zip | ||
|
||
The `zip` function combines multiple iterables element-wise into | ||
tuples, stopping at the shortest iterable. | ||
|
||
:signature: | ||
|
||
zip (*sequence1* *sequence2* #key *key1* *key2*) => (*zipped*) | ||
|
||
:parameter sequence1: | ||
|
||
An instance of :drm:`<sequence>`. | ||
|
||
:parameter sequence2: | ||
|
||
An instance of :drm:`<sequence>`. | ||
|
||
:parameter #key key1: | ||
|
||
An instance of :drm:`<function>`. Default value: | ||
:drm:`identity`. | ||
|
||
:parameter #key key2: | ||
|
||
An instance of :drm:`<function>`. Default value: | ||
:drm:`identity`. | ||
|
||
:value zipped: | ||
|
||
An instance of :drm:`<sequence>`. | ||
|
||
:description: | ||
|
||
The `zip` function is often classified as a *high-order function* and | ||
a *sequence transformation function*. | ||
|
||
The function operates on two sequences (like :drm:`range`, | ||
`array`, :drm:`list`, `deque` or `string`) and | ||
transforms them in a :drm:`<sequence>` of pairs, each element from | ||
one of the sequences. | ||
|
||
If *key1* or *key2* are not provided, they default to the function | ||
:drm:`identity` returning the object. If provided, this function | ||
transforms the element before collecting it. | ||
|
||
If the sequences provided have different lengths, func:`zip` | ||
automatically truncates the output to the length of the shorter | ||
sequence. | ||
|
||
|
||
:example: | ||
|
||
.. code-block:: dylan | ||
|
||
let a = #(1, 2); | ||
let b = #('a', 'b'); | ||
|
||
let zipped = zip(a, b); | ||
format-out("%=", zipped); // Output: #(#(1, 'a'), #(2, 'b')) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is fine, but I prefer the following style, because it doesn't have the distraction of the let a = #(1, 2);
let b = #('a', 'b');
zip(a, b)
// => #(#(1, 'a'), #(2, 'b')) |
||
|
||
:example: | ||
|
||
.. code-block:: dylan | ||
|
||
let a = #('a', 'b'); | ||
let b = #(3, 4); | ||
|
||
let zipped = zip(a, b, key2: odd?); | ||
format-out("%=", zipped); // Output: #(#('a', #t), ('b', #f)) | ||
|
||
zip-with | ||
^^^^^^^^ | ||
|
||
.. function:: zip-with | ||
|
||
|
||
:signature: | ||
|
||
zip-with (function collection #rest more-collections) ⇒ new-collection | ||
|
||
:parameter function: | ||
|
||
An instance of :drm:`<function>`. | ||
|
||
:parameter collection: | ||
|
||
An instance of :drm:`<collection>`. | ||
|
||
:parameter more-collections: | ||
|
||
An instance of :drm:`<collection>`. | ||
|
||
:value new-collection: | ||
|
||
An instance of :drm:`<collection>`. | ||
|
||
:example: | ||
|
||
.. code-block:: dylan | ||
|
||
let a = #(100, 200, 300); | ||
let b = #(1, 2, 3); | ||
|
||
let zipped = zip-with(\+, a, b); | ||
format-out("%=", zipped); // Output: #(101, 202, 303) | ||
|
||
:seealso: | ||
|
||
:drm:`map` | ||
|
||
zip-all | ||
^^^^^^^ | ||
|
||
.. function:: zip-all | ||
|
||
A :func:`zip` function that can take any number of iterables as | ||
parameters. | ||
|
||
:signature: | ||
|
||
zip-all (sequences) => (zipped) | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
:parameter sequences: | ||
|
||
An instance of :drm:`<sequence>`. | ||
|
||
:value zipped: | ||
|
||
An instance of :drm:`<sequence>`. | ||
|
||
:example: | ||
|
||
.. code-block:: dylan | ||
|
||
let a = #(1, 2, 3); | ||
let b = #('a', 'b', 'c'); | ||
let c = #(#t, #f, #t); | ||
|
||
let zipped = zip-all(a, b, c); | ||
|
||
format-out("%=", zipped); // Output: #(#(1, 'a', #t), #(2, 'b', #f), #(3, 'c', #t)) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -588,3 +588,29 @@ define method precedes?(elt-1, elt-2, seq :: <sequence>, | |
not-found; | ||
end block; | ||
end method precedes?; | ||
|
||
define function zip | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Naming is hard, so I won't claim that this is definitely better, but one potential alternative is to call this function |
||
(seq1 :: <sequence>, seq2 :: <sequence>, #key key1 = identity, key2 = identity) | ||
=> (zipped :: <sequence>) | ||
collecting (as <sequence>) | ||
for (e1 in seq1, e2 in seq2) | ||
collect(list(key1(e1), key2(e2))) | ||
end | ||
end; | ||
end; | ||
|
||
define constant zip-with | ||
= map; | ||
|
||
define function zip-all | ||
(#rest sequences) => (zipped :: <sequence>) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The name "sequences" is misleading here since this function will only work with lists. Should it work with sequences in general? (I think so.) Perhaps you can do it by calling |
||
local method recur (seqs, zipped :: <list>) | ||
if (any?(empty?, seqs)) | ||
reverse(zipped) | ||
else | ||
recur(map(tail, seqs), | ||
add(zipped, map(head, seqs))) | ||
end | ||
end; | ||
if (empty?(sequences)) #() else recur(sequences, #()) end | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am a big fan of the define function zip-all (#rest lists) => (zipped :: <list>)
iterate loop (lists = lists, zipped = #())
if (any?(empty?, lists))
reverse!(zipped)
else
loop(map(tail, lists), pair(map(head, lists)))
end if
end iterate
end function It basically combines the definition of the local method and the initial call to it. |
||
end; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would make these references to the types:
:drm:`<range>`
,:drm:`<vector>`
,:drm:`<list>`
,:drm:`<deque>`
, or:drm:`<string>`
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did it, in a previous commit, but wrong. Thanks.