folding and currying with Ruby - ruby

I'm exploring Functional Programming with the Ruby language. Below is my version of a Fold in Ruby. I've tested it on a variety of functions, reverse, filter, map etc, and it returns the results as expected. But it mutates data and needs assignment statements. Can anyone help me to do the same but without violating the Functional paradigm? Can anyone help me with the partial application of the curried function at the bottom? I suspect there something obvious I'm missing. Thanks.
fold_l = lambda do |ray, base, funcky|
if ray == []
base
else
base = funcky.call(base,ray.first)
ray.shift
fold_l.call(ray,base,funcky)
end
end
abc = [1, 2, 3, 4, 5, 6, 7]
mapper = lambda {|sum, x| sum << x*x}
lengthy = lambda {|sum, _| sum + 1}
p fold_l.call(abc,[],mapper) ## works fine
p abc ## but mutates data!!
abc = [1, 2, 3, 4, 5, 6, 7]
p curryFold = fold_l.curry.(abc).(0).(lengthy) ## works fine
lengthC = curryFold.(base:0).(funcky:lengthy)
p lengthC.call.(abc) ## but this gives error

Rework your fold_l function to not mangle the arguments its given:
def fold_l(ray, base, funcky)
return base if ray.empty?
base = funcky.call(base,ray.first)
fold_l(ray.last(ray.length-1),base,funcky)
end
This uses last to return a copy of the arguments minus the first. It's also not necessary to use lambda here as you want a named function, so you may as well declare it formally. lambda is reserved for situations where you don't necessarily have a name for it.
Note that in Ruby it's generally rude to damage the arguments your method's given unless there's an understanding that it's acceptable. Most methods make copies if they need to perform alterations.

I would probably implement foldl like this – and always be careful when using recursion in languages that don't support tail call optimisation (read more)
foldl = -> (f, acc, (x,*xs)) do
if x.nil? then
acc
else
foldl.call f, (f.call acc, x), xs
end
end
add = -> (x,y) do
x + y
end
length =
foldl.curry
. (-> (acc,_) { acc + 1 })
. (0)
data = [ 1, 2, 3, 4, 5 ]
p foldl.call add, 0, data
# => 15
p length.call data
# => 5
as per #tadman's recommendation, using a normal def/end block is probably better but that's just a matter of preference – note that currying is no longer necessary with this style
def foldl f, acc, (x,*xs)
if x.nil? then
acc
else
foldl f, (f.call acc, x), xs
end
end
def add x, y
x + y
end
def length xs
foldl (-> (acc,_) { acc + 1 }), 0, xs
end
data = [ 1, 2, 3, 4, 5 ]
p foldl method(:add), 0, data
# => 15
p length data
# => 5

Related

On a purely technical level, how does Pyret's `for` construct work?

I'm running through the "Tour of Pyret" for funsies, and noticed it's interesting way of handling for loops.
The prime example they give is
x = for map(elem from [list: 1,2,3,4]):
elem + 2
end
check:
x is [list: 3,4,5,6]
end
z = for filter(elem from [list: 1,2,3,4]):
elem < 3
end
check:
z is [list: 1,2]
end
y = for fold(sum from 0, elem from [list: 1,2,3]):
sum + elem
end
check:
y is 6
end
Which, upon reading deeper into the documentation could also just as easily have been expressed as
x = map(lam(elem): elem + 2 end, [list: 1, 2, 3, 4])
y = filter(lam(elem): elem < 3 end, [list: 1,2,3,4])
z = fold(lam(sum, elem): sum + elem end, 0, [list: 1, 2, 3])
So, for is a way to give a more loop-like structure to the more traditionally FP-esque list methods.
But it doesn't really explain how it works.
Can I put in any expression into a for, or are there certain limits?
Would I be able to create my own custom list function and use it in a for myself, or is it strictly limited to the builtins?

How to return a splat from a method in Ruby

I wanted to create a method for array's to get a splat of the array in return. Is this possible to do in Ruby?
For example here's my current code:
Array.module_eval do
def to_args
return *self
end
end
I expect [1,2,3].to_args to return 1,2,3 but it ends up returning [1,2,3]
You cannot return a "splat" from Ruby. But you can return an array and then splat it yourself:
def args
[1, 2, 3]
end
x, y, z = args
# x == 1
# y == 2
# z == 3
x, *y = args
# x == 1
# y == [2, 3]
Of course, this works on any array, so really there is no need for monkey patching a to_args method into Array - it's all about how the calling concern is using the splat operator:
arr = [1, 2, 3]
x, y, z = arr
x, *y = arr
*x, y = arr
Same mechanism works with block arguments:
arr = [1, 2, 3]
arr.tap {|x, *y| y == [2, 3]}
Even more advanced usage:
arr = [1, [2, 3]]
x, (y, z) = arr
The concept that clarifies this for me is that although you can simulate the return of multiple values in Ruby, a method can really return only 1 object, so that simulation bundles up the multiple values in an Array.
The array is returned, and you can then deconstruct it, as you can any array.
def foo
[1, 2]
end
one, two = foo
Not exactly. What it looks like you're trying to do (the question doesn't give usage examples) is to force multiple return values. However, returning the splatted array self may do exactly what you need, as long as you're properly handling multiple return values on the calling side of the equation.
Consider these examples:
first, *rest = [1, 2, 3] # first = 1, rest = [2, 3]
*rest, last = [1, 2, 3] # rest = [1, 2], last = 3
first, *rest, last = [1, 2, 3] # first = 1, rest = [2], last = 3
Other than this, I can't actually see any way to capture or pass along multiple values like you're suggesting. I think the answer for your question, if I understand it correctly, is all in the caller's usage.

Splat operator in Ruby (a quicksort example)

Hello I'm studying some Ruby code. Implement Quicksort in Ruby:
1 def qsort(lst)
2 return [] if lst.empty?
3 x, *xs = *lst
4 less, more = xs.partition{|y| y < x}
5 qsort(less) + [x] + qsort(more)
6 end
Given:
lst = [1, 2, 3, 4, 5]
x, *xs = *lst
I do not know if I understand what line 3 is doing correctly:
From my observation and experiment, this will assign 1 from lst to x, and the rest of lst to xs.
Also I found these two are doing the same thing:
x, *xs = *lst
is equivalent to
x, *xs = lst
My question is, what's the name of this nice feature (I will edit the title afterwards to adapt)? Then I could study more about this Ruby feature myself. Sorry if it's a duplicate problem, because I don't know the keyword to search on this problem.
The name of this feature is called splat operator in Ruby.
The splat operator in Ruby, Groovy and Perl allows you to switch between parameters and arrays:it splits a list in a series of parameters,or collects a series of parameters to fill an array.
From 4 Lines of Code.
This statement
x, *xs = *lst
doesn't make much sense to me, but these do:
x, *xs = [1, 2, 3] # x -> 1, xs -> [2, 3]
x = 1, *[2, 3, 4] # x -> [1, 2, 3, 4]
this usage IMO has nothing to do with parameters, but as others said splat can be (and usually is) used with parameters:
def foo(a, b, c)
end
foo(*[1,2,3]) # a -> 1, b -> 2, c -> 3

How do I filter elements in an array?

Sample Array:
x = [1,2,3,4,2,2,2]
Filter:
y = [2,4,7,9]
Desired output:
result = [2,4,2,2,2]
I tried:
result = (x & y)
but this gives me [4,2].
How do I get: result = [2,4,2,2,2]?
How about:
x - (x - y)
#=> [2, 4, 2, 2, 2]
1-2 lines longer than #Mark's answer, but more efficient (if both arrays are large):
require 'set'
keep = Set[2,4,7,9] # or Set.new(some_large_array)
result = x.select{ |n| keep.include?(n) } #=> [2, 4, 2, 2, 2]
The problem with writing...
x.select{ |i| y.include?(i) }
...is that this is O(x*y) the the number of elements in each array. With 100 elements in each you are doing 10,000 operations in the worst case; my answer does only 100 operations.
First, don't capitalize variables in Ruby. Capitalization is for constants, like class names.
result = x.select {|i| y.include? i}
Note that select is also called find_all, and is the positive filter in ruby; the negative filter is reject. Between the braces you can put any code you want; it will be run once for each item of x (the item is passed in as an argument and becomes i), and the result of the whole call will include all the elements for which the block returns a true value.

List comprehension in Ruby

To do the equivalent of Python list comprehensions, I'm doing the following:
some_array.select{|x| x % 2 == 0 }.collect{|x| x * 3}
Is there a better way to do this...perhaps with one method call?
How 'bout:
some_array.map {|x| x % 2 == 0 ? x * 3 : nil}.compact
Slightly cleaner, at least to my taste, and according to a quick benchmark test about 15% faster than your version...
If you really want to, you can create an Array#comprehend method like this:
class Array
def comprehend(&block)
return self if block.nil?
self.collect(&block).compact
end
end
some_array = [1, 2, 3, 4, 5, 6]
new_array = some_array.comprehend {|x| x * 3 if x % 2 == 0}
puts new_array
Prints:
6
12
18
I would probably just do it the way you did though.
I made a quick benchmark comparing the three alternatives and map-compact really seems to be the best option.
Performance test (Rails)
require 'test_helper'
require 'performance_test_help'
class ListComprehensionTest < ActionController::PerformanceTest
TEST_ARRAY = (1..100).to_a
def test_map_compact
1000.times do
TEST_ARRAY.map{|x| x % 2 == 0 ? x * 3 : nil}.compact
end
end
def test_select_map
1000.times do
TEST_ARRAY.select{|x| x % 2 == 0 }.map{|x| x * 3}
end
end
def test_inject
1000.times do
TEST_ARRAY.inject([]) {|all, x| all << x*3 if x % 2 == 0; all }
end
end
end
Results
/usr/bin/ruby1.8 -I"lib:test" "/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake/rake_test_loader.rb" "test/performance/list_comprehension_test.rb" -- --benchmark
Loaded suite /usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake/rake_test_loader
Started
ListComprehensionTest#test_inject (1230 ms warmup)
wall_time: 1221 ms
memory: 0.00 KB
objects: 0
gc_runs: 0
gc_time: 0 ms
.ListComprehensionTest#test_map_compact (860 ms warmup)
wall_time: 855 ms
memory: 0.00 KB
objects: 0
gc_runs: 0
gc_time: 0 ms
.ListComprehensionTest#test_select_map (961 ms warmup)
wall_time: 955 ms
memory: 0.00 KB
objects: 0
gc_runs: 0
gc_time: 0 ms
.
Finished in 66.683039 seconds.
15 tests, 0 assertions, 0 failures, 0 errors
There seems to be some confusion amongst Ruby programmers in this thread concerning what list comprehension is. Every single response assumes some preexisting array to transform. But list comprehension's power lies in an array created on the fly with the following syntax:
squares = [x**2 for x in range(10)]
The following would be an analog in Ruby (the only adequate answer in this thread, AFAIC):
a = Array.new(4).map{rand(2**49..2**50)}
In the above case, I'm creating an array of random integers, but the block could contain anything. But this would be a Ruby list comprehension.
I discussed this topic with Rein Henrichs, who tells me that the best performing solution is
map { ... }.compact
This makes good sense because it avoids building intermediate Arrays as with the immutable usage of Enumerable#inject, and it avoids growing the Array, which causes allocation. It's as general as any of the others unless your collection can contain nil elements.
I haven't compared this with
select {...}.map{...}
It's possible that Ruby's C implementation of Enumerable#select is also very good.
I've just published the comprehend gem to RubyGems, which lets you do this:
require 'comprehend'
some_array.comprehend{ |x| x * 3 if x % 2 == 0 }
It's written in C; the array is only traversed once.
An alternative solution that will work in every implementation and run in O(n) instead of O(2n) time is:
some_array.inject([]){|res,x| x % 2 == 0 ? res << 3*x : res}
Enumerable has a grep method whose first argument can be a predicate proc, and whose optional second argument is a mapping function; so the following works:
some_array.grep(proc {|x| x % 2 == 0}) {|x| x*3}
This isn't as readable as a couple of other suggestions (I like anoiaque's simple select.map or histocrat's comprehend gem), but its strengths are that it's already part of the standard library, and is single-pass and doesn't involve creating temporary intermediate arrays, and doesn't require an out-of-bounds value like nil used in the compact-using suggestions.
This is more concise:
[1,2,3,4,5,6].select(&:even?).map{|x| x*3}
[1, 2, 3, 4, 5, 6].collect{|x| x * 3 if x % 2 == 0}.compact
=> [6, 12, 18]
That works for me. It is also clean. Yes, it's the same as map, but I think collect makes the code more understandable.
select(&:even?).map()
actually looks better, after seeing it below.
Ruby 2.7 introduced filter_map which pretty much achieves what you want (map + compact):
some_array.filter_map { |x| x * 3 if x % 2 == 0 }
You can read more about it here.
Like Pedro mentioned, you can fuse together the chained calls to Enumerable#select and Enumerable#map, avoiding a traversal over the selected elements. This is true because Enumerable#select is a specialization of fold or inject. I posted a hasty introduction to the topic at the Ruby subreddit.
Manually fusing Array transformations can be tedious, so maybe someone could play with Robert Gamble's comprehend implementation to make this select/map pattern prettier.
Something like this:
def lazy(collection, &blk)
collection.map{|x| blk.call(x)}.compact
end
Call it:
lazy (1..6){|x| x * 3 if x.even?}
Which returns:
=> [6, 12, 18]
Another solution but perhaps not the best one
some_array.flat_map {|x| x % 2 == 0 ? [x * 3] : [] }
or
some_array.each_with_object([]) {|x, list| x % 2 == 0 ? list.push(x * 3) : nil }
This is one way to approach this:
c = -> x do $*.clear
if x['if'] && x[0] != 'f' .
y = x[0...x.index('for')]
x = x[x.index('for')..-1]
(x.insert(x.index(x.split[3]) + x.split[3].length, " do $* << #{y}")
x.insert(x.length, "end; $*")
eval(x)
$*)
elsif x['if'] && x[0] == 'f'
(x.insert(x.index(x.split[3]) + x.split[3].length, " do $* << x")
x.insert(x.length, "end; $*")
eval(x)
$*)
elsif !x['if'] && x[0] != 'f'
y = x[0...x.index('for')]
x = x[x.index('for')..-1]
(x.insert(x.index(x.split[3]) + x.split[3].length, " do $* << #{y}")
x.insert(x.length, "end; $*")
eval(x)
$*)
else
eval(x.split[3]).to_a
end
end
so basically we are converting a string to proper ruby syntax for loop
then we can use python syntax in a string to do:
c['for x in 1..10']
c['for x in 1..10 if x.even?']
c['x**2 for x in 1..10 if x.even?']
c['x**2 for x in 1..10']
# [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# [2, 4, 6, 8, 10]
# [4, 16, 36, 64, 100]
# [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
or if you don't like the way the string looks or having to use a lambda we could forego the attempt to mirror python syntax and do something like this:
S = [for x in 0...9 do $* << x*2 if x.even? end, $*][1]
# [0, 4, 8, 12, 16]
https://rubygems.org/gems/ruby_list_comprehension
shameless plug for my Ruby List Comprehension gem to allow idiomatic Ruby list comprehensions
$l[for x in 1..10 do x + 2 end] #=> [3, 4, 5 ...]
I think the most list comprehension-esque would be the following:
some_array.select{ |x| x * 3 if x % 2 == 0 }
Since Ruby allows us to place the conditional after the expression, we get syntax similar to the Python version of the list comprehension. Also, since the select method does not include anything that equates to false, all nil values are removed from the resultant list and no call to compact is necessary as would be the case if we had used map or collect instead.

Resources