Created
March 25, 2013 02:16
-
-
Save d11wtq/5234515 to your computer and use it in GitHub Desktop.
Lists, and the concept of "head" and "tail", coupled with recursion are at the core of functional programming. This demonstrates how you can do it in Ruby, without ever writing a loop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# foldl() is fundamental. With a fold, you can do everything else. | |
def foldl(list, acc, &fn) | |
if list == [] # base case, return the accumulator | |
acc | |
else | |
head, *tail = list | |
foldl(tail, fn.call(acc, head), &fn) #recurse on the remainder | |
end | |
end | |
# now we can write reverse() | |
def reverse(list) | |
foldl(list, []) {|acc, v| [v, *acc]} | |
end | |
# and we can write foldr (reverse foldl) | |
def foldr(list, acc, &fn) | |
foldl(reverse(list), acc, &fn) | |
end | |
# we can write map | |
def map(list, &fn) | |
reverse(foldl(list, []) {|acc, v| [fn.call(v), *acc]}) | |
end | |
# we can write foreach | |
def foreach(list, &fn) | |
if list != [] | |
head, *tail = list | |
fn.call(head) | |
foreach(tail, &fn) | |
end | |
end | |
# etc | |
foldl([1, 2, 3], 0) {|acc, n| acc + n} # => 6 | |
reverse([1, 2, 3]) # => [3, 2, 1] |
@d11wtq Totally, those splats are awesome, I can't believe how clean that code is. I doubt it would be any more concise if written in Haskell.
@matthayter without pattern matching you have the annoying conditionals, which is slightly less clean than the haskell/erlang equivalent:
foldl(_, Acc, []) -> Acc;
foldl(Fn, Acc, [Head|Tail]) ->
foldl(Fn, Fn(Acc, Head), Tail).
RubyVM::InstructionSequence.compile_option = {
tailcall_optimization: true,
trace_instruction: false,
}
https://speakerdeck.com/jeg2/10-things-you-didnt-know-ruby-could-do?slide=46
(not really practical, only available in a sub-parser, I think.)
@pda wow, that's interesting. Presumably that will find its way into the default set of compile options then.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@matthayter yeah absolutely, but it was fun to observe the the splat really simplifies doing head|tail and cons operations in Ruby, without mutating data.