Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@andkerosine
Created August 15, 2012 05:56
Show Gist options
  • Save andkerosine/3356675 to your computer and use it in GitHub Desktop.
Save andkerosine/3356675 to your computer and use it in GitHub Desktop.
Haskell-like list comprehensions in Ruby
$stack, $draws = [], {}
def method_missing *args
return if args[0][/^to_/]
$stack << args.map { |a| a or $stack.pop }
$draws[$stack.pop(2)[0][0]] = args[1] if args[0] == :<
end
class Array
def +@
$stack.flatten!
keys = $draws.keys & $stack
draws = $draws.values_at *keys
comp = draws.shift.product(*draws).map do |draw|
$stack.map { |s| draw[keys.index s] rescue s }.reduce do |val, cur|
op = Symbol === cur ? [:send, :method][val.method(cur).arity] : :call
val.send op, cur
end
end
$stack, $draws = [], {}
Symbol === last ? comp.select(&pop) : comp
end
def -@
case map(&:class).index Range
when 0 then first.to_a
when 1 then [first] + last.step(last.min.ord - first.ord).to_a
else self
end
end
end
foo =+ [x * y | x <- [1..3], y <- [4..6]]
# [4, 5, 6, 8, 10, 12, 12, 15, 18]
bar =+ [a + b | a <- ['n','p'..'t'], b <- %w[a i u e o]]
# ["na", "ni", "nu", "ne", "no", "pa", "pi", "pu", "pe", "po", "ra", "ri", "ru", "re", "ro", "ta", "ti", "tu", "te", "to"]
baz =+ [i ** 2 / 3 | i <- [3,6..100], :even?]
# [12, 48, 108, 192, 300, 432, 588, 768, 972, 1200, 1452, 1728, 2028, 2352, 2700, 3072]
quux =+ [s.size.divmod(2) | s <- %w[Please do not actually use this.]]
# [[3, 0], [1, 0], [1, 1], [4, 0], [1, 1], [2, 1]]
@styx
Copy link

styx commented Aug 18, 2012

👍

@towynlin
Copy link

Wow, totally ridonkulous. Nicely done!

@rdp
Copy link

rdp commented Aug 20, 2012

now if it could avoid using method_missing somehow...

@gsinclair
Copy link

Wow, I'm amazed. I really wish Ruby supported this style.

@thejoecarroll
Copy link

Nice work. Let's see if this can be made fly with 2.0...

@xatier
Copy link

xatier commented May 12, 2013

Really cool!

@pkondzior
Copy link

Yuck

@dansimco
Copy link

Bonus points for the name!

@appplemac
Copy link

Amazing! Some comments would be useful, though.

@RichardFreeman
Copy link

so cool!

@vseloved
Copy link

So, what about using it in place of function argument?

Like:
[x * y | x <- [1..3], y <- [4..6]].each { |a| print a }

@zh4ngx
Copy link

zh4ngx commented May 12, 2013

Ruby 2.1!

@linduxed
Copy link

Looks really nice!

@freakhill
Copy link

I played a bit with the idea to get that. Trying out ruby2 refinements. Too lazy for the "|" support but can do conditions.
https://gist.github.com/freakhill/5564328

@igbanam
Copy link

igbanam commented May 12, 2013

+1 for "Raskell". Pun in "Rascal"?

@darth10
Copy link

darth10 commented May 13, 2013

Awesome job! ❤️ Gem it please?

@wteuber
Copy link

wteuber commented May 15, 2013

"Haskell-like" without lambdas?
Even if it doesn't look as much like Haskell, this snippet is much closer to the Haskell principles, without abusing Ruby.

foo = ->(x=(1..3), y=(4..6)){x.flat_map{|a| y.map{|b| a*b}}}.call

(Of course you usually wouldn't need to call any lambdas before actually executing the application)

@hauleth
Copy link

hauleth commented May 18, 2013

Nice but for Ruby I prefer using Ruby syntax:

(1..3).zip(4..6).map { |a, b| a * b }

@chrislerum
Copy link

I'd humbly like to up-vote the previous comment.

@whistler
Copy link

Thats quite brilliant, the author does say "Please do not actually use this."
@hauleth zip is a bit different, list comprehensions are more like cross products:

(1..3).to_a.product((4..6).to_a).map{|a,b| a*b}

or you could do a double map_flat like @wteuber.

@josiah14
Copy link

Impressive.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment