Proc.new i przezkazywanie parametrów

Będę wdzięczny za wyjaśnienie następującego kodu:

fruit = ->(str) { ['banan', 'apple', 'grape'].include? str }
vege = ->(str) { ['pomidor', 'ziemniak', 'marchewka'].include? str }
miecho = ->(str) {['ciele', 'mele', 'baran'].include? str }

['apple', 'pomidor', 'ciele', 'nic'].each do |food|
  case food
    when fruit then puts "#{food} to jest fruit"
    when vege then puts "#{food} to jednav vege"
    when miecho then puts "#{food} to jednak miecho"
    else p "nie wiem co to za #{food}"
  end
end

Wiem, że fruit, vege i miecho to obiekty Proc,
przy when mam fruit, miecho i vege, ale nie wiem jak ruby przekazuje argument food, tak że fruit, czy vege czy miecho “wie”, że chodzi o konkretny element z tablicy. Jak wpiszę:

  (...)  when fruit(food) then puts "#{food} to jest fruit" (...)

to dostaję:

proc2.rb:17:in `block in <main>': undefined method `fruit' for main:Object (NoMethodError)
    from proc2.rb:15:in `each'
    from proc2.rb:15:in `<main>'

Będę wdzięczny za wskazówki :wink:

Na jakiej wersji rubiego to uruchamiasz ?

ruby 2.0.0p481 (2014-05-08 revision 45883) [universal.x86_64-darwin14] powinno też działać na 1.9.3

@paki w drugim przykładzie z błędem - powinno być:

(...)  when **fruit(food)** then puts "#{food} to jest fruit" (...)

Tutaj masz opisane jak to działa http://batsov.com/articles/2013/09/24/lambdas-slash-procs-in-case-expressions/

1 Like

Formalnie co case robi to bierze argument z when i porównuje go z tematem (tym czymś po case) przy pomocy porównania ===

Czyli:

case temat
  when wariant1 then puts "1"
  when wariant2 then puts "2"
end

co następuje to ruby robi wariant1 === temat i jeżeli się zgadza to wypisuje 1

Teraz jest sporo klas które definiują === co więcej można ją samemu zdefiniować. Ale są pewne wbudowane zachowania o których warto wiedzieć:

  • W przypadku Klas [Class] operator === zachowuje się jak is_a? czyli sprawdza czy temat jest instancją danej klasy
  • W przypadku przedziałów Range np. (1…10) === zachowuje się jak include?
  • W przypadku wyrażeń regularnych Regexp np. /foo/ zachowuje się jak match?
  • W przypadku lambda/proc Lambda zachowuje się jak call czyli wywołuję lambdę/proca

Ps. Jako ciekawostka operator === używany jest także w wyrażeniu begin ... rescue Exception ... end możemy stworzyć własną klasę Pokeball która będzie łapać wszystkie wyjątki poprzez zdefiniowanie ją jako

class Pokeball
  def self.===(exception) true end
end

begin
  raise "some noise!"
rescue Pokeball
  # Gotta catch them all!
end
1 Like

@swistak84 dzięki za wyjaśnienia, @paki dzięki za link to mi rozjaśniło temat, rozumiem, że obiekty Proc z jednym argumentem zadziałają przy case, ale już z dwoma lub więcej argumentami już raczej nie? Mi pojawia się błąd

is_ev = ->(n,m) { puts n, m }    
is_ev === 2,3

# => SyntaxError: (irb):22: syntax error, unexpected ',', expecting end-of-input

irb(main):023:0> is_ev === 2
#=> ArgumentError: wrong number of arguments (1 for 2)

ale być może, źle tego używam w tym wypadku i powinienem użyć czegoś innego?

W tym przypadku możesz użyc tablicy:

2.2.2 :001 > is_ev = -> (n) { puts n }
 => #<Proc:0x0000000261c590@(irb):1 (lambda)> 
2.2.2 :002 > is_ev === [2,3,4]
2
3
4
 => nil

no tak, ale nadal proc otrzymuje tylko jeden argument, a nie kilka - ale być może na siłę szukam problemu, tam gdzie go nie ma. Dzięki za wyjaśnienia - wiem więcej teraz niż rano :smiley:

Proc musi przyjmować jeden argument - temat case’a. Co miało by być drugim argumentem?
Poczytaj, pomyśl, wyda ci się to oczywiste.