Ruby functions: Difference between revisions
(One intermediate revision by the same user not shown) | |||
Line 185: | Line 185: | ||
</blockquote><!-- yield --> | </blockquote><!-- yield --> | ||
</blockquote><!-- block --> | </blockquote><!-- block --> | ||
= currying = | |||
<blockquote> | |||
<source lang="ruby"> | |||
log_user = lambda { |firstname, lastname, time| puts("user: #{firstname} #{lastname}, time: #{time}") } | |||
log_vader = log_user.curry["darth", "vader"] | |||
log_vader.call(Time.now) # log vader at time.. | |||
log_vader.call(Time.now) # log vader at time.. | |||
</source> | |||
</blockquote><!-- currying --> | |||
= nested block pipelines = | = nested block pipelines = | ||
<blockquote> | <blockquote> | ||
<source lang="ruby"> | <source lang="ruby"> | ||
def pipeline(&block) | |||
.reverse | |||
.reduce(block) { |nested_blk, fn_blk| lambda { fn_blk.call(&nested_blk) } } | |||
.call | |||
end | end | ||
pipeline([ | |||
[ | lambda { |&block| assert_logs(:info, "abc", &block) }, | ||
{ | lambda { |&block| assert_logs(:info, "def", &block) }, | ||
{ | ]) do | ||
] | log(:info, "abc") | ||
log(:info, "def") | |||
end | end | ||
</source> | </source> | ||
</blockquote><!-- pipelines --> | </blockquote><!-- pipelines --> |
Latest revision as of 16:48, 30 June 2023
invocation
Parentheses are optional, but very helpful.
get_greetings "alex" get_greetings "alex", "will" get_greetings("alex", "will")
return
In ruby, the last statement is always the return value.
def printhi puts "hi" endYou can also use
return
to break out early from a function.def get_greeting(name) if name == 'will' return "hello me, it's me again" "hello " + name end
function suffix conventions
Ruby has a convention of using suffixes in function names to indicate specific behaviours.
These suffixes have no syntactic significance, they are simply regular characters and a part of the function name.
method?
returns a bool
method!
permanent, or dangerous operation.
normally means modifying the instance, instead of returning new instance.
Enumerable#sort
returns a sorted version of the object whileEnumerable#sort!
sorts it in place- In Rails,
ActiveRecord::Base#save
returns false if saving failed, whileActiveRecord::Base#save!
raises an exception.Kernel::exit
causes a script to exit, whileKernel::exit!
does so immediately, bypassing any exit handlers.
wrapping functions
Ruby supports 4x types of arguments and an optional
do
block.
This is how you can wrap all of them.def foo(a, b=1, *c, d:, **e) puts a, b, c, d, e yield if block_given? end def wrapper(*args, **kwargs, &block) foo(*args, **kwargs, &block) end
procs
Procs behave similarly to lambdas. They are code-blocks that are executed when their method
call
is called.# anynymous function proc = Proc.new { puts("hello") } proc.call proc = Proc.new { |x, y| puts(x, y) } proc.call(1, 2)# &:method refers to a method you want to call on an object # # for example, this calls method 'upcase' on each input object ex: {x}.upcase ["alex", "courtney", "sam"].map(&:upcase)If filtering, you can do the opposite
[1, 2, 3, 4].filter(&:even?) # even numbers [1, 2, 3, 4].filter {|x| !x.even? } # odd numbers
lambdas
foo = lambda { puts "bar" } foo = -> { puts "bar" } foo.call() foo = lambda { |x| puts x } foo = ->(x) { puts x } foo.call("bar")Lambda can also work as partials, providing defaults to keyword arguments.
foo = lambda { |x="abc"| puts x }Currying
def foo(a, b, c) puts a, b, c end fn = lambda { |b, c| foo("a", b, c) } fn.call("b", "c")
blocks
&block
Using &block is the same as yield, you just declare a name for the callback you are invoking.
def blocker(&block) block.call puts "you" end blocker { puts "hello" } #> hello #> you blocker do puts "hello" end #> hello #> youyield
Yield without args
def yielder yield puts "you" end yielder { puts "hello" } #> hello #> you yielder do puts "hello" end #> hello #> youYield with args
def yielder yield("you") end yielder { |x| puts "hello, #{x}" } #> hello, you yielder do |x| puts "hello, #{x}" end #> hello, youCheck if a yield block was provided using
block_given?
.def yielder yield if block_given? end
currying
log_user = lambda { |firstname, lastname, time| puts("user: #{firstname} #{lastname}, time: #{time}") } log_vader = log_user.curry["darth", "vader"] log_vader.call(Time.now) # log vader at time.. log_vader.call(Time.now) # log vader at time..
nested block pipelines
def pipeline(&block) .reverse .reduce(block) { |nested_blk, fn_blk| lambda { fn_blk.call(&nested_blk) } } .call end pipeline([ lambda { |&block| assert_logs(:info, "abc", &block) }, lambda { |&block| assert_logs(:info, "def", &block) }, ]) do log(:info, "abc") log(:info, "def") end
params
positional params
def print_greeting(name) puts "hello #{name}" endkeyword params
c
is a required keyword argument (no default)d
is a keyword argument with a defaultdef foo(a, b, c:, d: nil) puts bar end foo(1, 2, c: 3, d: 4) foo 1, 2, c: 3, d: 4hash params
- before keyword params were introduced, you could use hash params
def foo(options = {}) bar = options.fetch(:bar, 'default') puts bar end foo(bar: 'baz') # => 'baz'variable num of params (splat)
def print_greetings(*names) names.each { |name| puts "hello #{name}" } endunpack hash as keyword arguments (double-splat)
def foo(**bar) puts bar end foo(a: 1, b: 2, c: 3) #> {:a=>1, :b=>2, :c=>3}
argument type order
def testing( pos, eq = 1, *args, kwd: 1, **kwargs ) puts "hi" endsuper wrapper
*
used alone accepts and ignores all arguments.
Note thatsuper
will receive all of the original arguments automatically.class Foo def multiprint(a, b) puts a, b end end class Bar < Foo def multiprint(*) super end end Bar.new.multiprint(1, 2) # 1 # 2