date: "2024-11-18"
Categories: ["Development", "ruby", "rubylang"]
Tags: ["Development", "Ruby", "Functional"]
at = -> k, a { a[k] }.curry
get = -> meth, a {a.send(meth)}.curry
first = get.(:first)
last = get.(:last)
# @param [a->a] printer: is a function that takes a string and prints it
# @param [Integer] slice: is the number of elements to process before printing
# @return [Enumerable]: is the collection to process
view_progress = ->printer, slice, enum {
start_time = Time.now
index = 0
obj = Object.new
total = enum.count
obj.define_singleton_method(:each) do |&block|
enum.each do |a|
index += 1
if (index % slice) == 0
elapsed = Time.now - start_time
avg = index / elapsed.to_f
total_time = (total.to_f / avg).to_i
printer.("#{index} / #{total}. Elapsed: #{elapsed.to_i}sec. Rate: #{"%0.2f" % avg} entry/sec. Estimated completion time #{start_time + total_time}")
end
block.call(a)
a
end
end
obj.extend(Enumerable)
obj
}.curry
overwriter = ->a { print("\r" + a) ; a }
my_progress = view_progress.(overwriter)
log = ->a { logger.info(a); a}
my_progress = view_progress.(log)
# Usage
10.times.then(&my_progress.(10)).each{|a| sleep 0.05}
Ruby lambdas can be used for documenting code by adding a name to an algorithm.
users.map { |u| u.first_name = " " + u.last_name }
You can use a lambda to make it a bit more clear.
full_name = -> a { a.first_name + a.last_name }
users.map(&full_name)
In this case &
calls the to_proc
method on lambda.
Here’s another example:
users.select { |a| a.age > 18 }
It can be refactored to
adult = -> a { a.age > 18 }
users.select(&adult)
You can then reuse this adult lambda in combination with other lambdas.
male = -> a { a.gender == :male }
users.select { |a| adult.(a) && male.(a) }
Lambda can also be used for aliasing a method with a name more close to the current context.
If we look at this code:
key = Digest::MD5.hexdigest(Digest::MD5.hexdigest(login) + Digest::MD5.hexdigest(user.keyword))
It can be hard to read, but lets extract the md5 calculation in a lambda:
md5 = -> str { Digest::MD5.hexdigest(str) }
key = md5.(md5.(login) + md5.(user.keyword))
This makes the code clearer and more concise. The signal to noise ratio is much higher this time.
Another way of doing this would be to create a method on the current class. However this is more code and the md5 method definition would be far from its call location even if the method would be use only on the caller method.
When we say simple we don’t mean less powerful.