Coming from Python, I like many of the features that Coffeescript borrows from Python and Perl (ranges/slices, comprehensions, destructuring assignments). Is there any syntactic sugar in Coffeescript to mimic Python's enumerate开发者_运维问答
or zip
(itertools.izip
) functions?
Here are the patterns that I don't care much for:
# an enumerate call would be helpful here
i = 0
for x in arr
... use x and i ...
i++
and
# a zip would be useful here
n = Math.min(arr1.length,arr2.length)
for i in 0...n
x = arr1[i]; y = arr2[i]
... use x and y ...
forEach is effectively built in:
a = ['a','b','c']
for el,i in a
alert "Element #{el} is at index #{i}"
Enumerate:
arr.forEach (x, i) ->
... use x and i ...
zip
/ zipWith
(I learned these from Haskell; I assume they mean the same thing in Python?):
zip = (arr1, arr2) ->
basic_zip = (el1, el2) -> [el1, el2]
zipWith basic_zip, arr1, arr2
zipWith = (func, arr1, arr2) ->
min = Math.min arr1.length, arr2.length
ret = []
for i in [0...min]
ret.push func(arr1[i], arr2[i])
ret
Some examples (tested):
zip([1, 2, 3], [4, 5, 6]) # => [[1, 4], [2, 5], [3, 6]]
add = (el1, el2) -> el1 + el2
zipWith(add, [1, 2, 3], [4, 5, 6]) # => [5, 7, 9]
Update: Reimplemented Haskell-style, just for fun. Not as cool without the pattern matching, but oh well..
zipWith = (func, arr1, arr2) ->
return [] if arr1.length is 0 or arr2.length is 0
el1 = arr1.shift()
el2 = arr2.shift()
ret_arr = zipWith func, arr1, arr2
ret_arr.unshift func(el1, el2)
ret_arr
Oh, man, this was fun. SO needs more questions like this :D
Gist for zip
and zipWith
For zipping and other such utility functions, Underscore.js is pretty much the standard library—and it happens to have been created by Jeremy Ashkenas, the man behind CoffeeScript. With it, you could write your zip example as
for elems in _.zip(arr1, arr2)
x = elems[0]; y = elems[1]
...
or better yet
for [x, y] in _.zip(arr1, arr2)
...
using pattern-matching. Note, however, that _.zip
uses the max length of arr1
and arr2
, not the min; so if you don't want to handle undefined
values, you should truncate the longer array first.
There's also a CoffeeScript implementation of Underscore, Underscore.coffee, which is a great place to look if you're wondering how to implement a particular loop in CoffeeScript.
The CoffeeScript Cookbook lists a nice implementation of a zip function:
# Usage: zip(arr1, arr2, arr3, ...)
zip = () ->
lengthArray = (arr.length for arr in arguments)
length = Math.min(lengthArray...)
for i in [0...length]
arr[i] for arr in arguments
zip([0, 1, 2, 3], [0, -1, -2, -3])
# => [[0, 0], [1, -1], [2, -2], [3, -3]]
http://coffeescriptcookbook.com/chapters/arrays/zip-function
Don't forget that CoffeeScript is just an alternate syntax for ECMAScript. At least for your first example, there is a perfectly good ECMAscript function (Array.prototype.forEach
), which already does what you want:
arr = ["a", "b", "c"]
arr.forEach (el, i) ->
alert "Element #{el} is at index #{i}"
Unfortunately, there is no Array.prototype.zip
or Array.prototype.zipWith
. That seems to be a pretty big omission, especially considering that there is both reduce
and reduceRight
, the latter of which many other languages don't have. My guess is that it is a simple oversight, and we are going to see zip
in some future version of the language.
For zip, try this one:
zip = (x...) ->
(y[i] for y in x for i in [0...Math.min (y.length for y in x)...])
If you prefer to zip all the way to the end, use Math.max()
.
精彩评论