Ruby'de golf oynamak için hangi genel ipuçlarını verebilirsiniz?
Genel olarak Ruby'ye özgü golf problemlerini kodlamak için uygulanabilecek fikirler arıyorum. (Örneğin, "Yorumları kaldır" bir cevap olmaz.)
Lütfen cevap başına bir ipucu gönderin.
Ruby'de golf oynamak için hangi genel ipuçlarını verebilirsiniz?
Genel olarak Ruby'ye özgü golf problemlerini kodlamak için uygulanabilecek fikirler arıyorum. (Örneğin, "Yorumları kaldır" bir cevap olmaz.)
Lütfen cevap başına bir ipucu gönderin.
Yanıtlar:
?d
için ?~
1.8 içinde.$><<"string"
bundan daha kısadır print"string"
.$<.map{|l|...}
, bundan daha kısadır while l=gets;...;end
. Ayrıca $<.read
hepsini bir kerede okumak için kullanabilirsiniz .$<
ve gets
eğer dosya adınız varsa stdin yerine bir dosyadan okuyacaksınız ARGV
. Yani yeniden uygulamaya golfiest yolu cat
olacaktır: $><<$<.read
.cat
da yakut dosyasını tamamen boş bırakmak (0 byte) ve -p
bayrakla birlikte komut satırından çalıştırılması konusunda ısrar etmektir .
puts *$<
☺
ya ♫
, yoksa yeterince çılgın ise:?﷽.ord=65021
Bir dizinin kuyruğunu ve başını almak için uyarıcı operatörünü kullanın:
head, *tail = [1,2,3]
head => 1
tail => [2,3]
Bu aynı zamanda diğer şekilde de çalışır:
*head, tail = [1,2,3]
head => [1,2]
tail => 3
*
Öğeleri birleştirmek için yöntemi bir dizideki bir dizeyle kullanın :
[1,2,3]*?,
=> "1,2,3"
abort
Programı sonlandırmak ve standart hataya bir dize yazdırmak için - daha kısa puts
izlediexit
gets
, daha sonra ~/$/
uzunluğunu bulmak için kullanabilirsiniz (eğer varsa izleyen bir yeni satırı saymaz)[]
Bir dize başka içerip içermediğini kontrol etmek için kullanın :'foo'['f'] #=> 'f'
tr
yerine gsub
karakter akıllıca değiştirmeler için:'01011'.tr('01','AB') #=> 'ABABB'
chop
yerinechomp
abort
ve~/$/
~/$/
gets
, sonucu $_
değişkende saklanır . /regex/ ~= string
ilk eşleşmenin dizinini döndürür. ~
Bir regex'e çağrı yapmak eşdeğerdir /regex/ ~= $_
. Yani böyle bir şey olurdus=gets;l= ~/$/
end
.end
Kodunuzdan kaldırmayı deneyin .
def...end
İşlevleri tanımlamak için kullanmayın . Ruby 1.9'daki yeni -> operatörüyle bir lambda yapın. (-> operatörü bir "stabby lambda" veya "çizgi roketi" dir .) Bu, işlev başına 5 karakter kaydeder.
# 28 characters
def c n
/(\d)\1/=~n.to_s
end
# 23 characters, saves 5
c=->n{/(\d)\1/=~n.to_s}
Yöntem çağrıları c n
veya c(n)
. Lambda aramaları c[n]
. Her birinin maliyeti 1 karakter olarak değiştirildiği c n
için c[n]
, c n
5 kereden fazla kullanabiliyorsanız , yöntemi saklayın.
do...end
Blokları alan tüm yöntemler {...}
bunun yerine blokları alabilir . Bu 3 ila 5 karakter kaydeder. Önceliği {...}
çok yüksekse, düzeltmek için parantez kullanın.
# 48 characters
(?a..?m).zip (1..5).cycle do|a|puts a.join','end
# WRONG: passes block to cycle, not zip
(?a..?m).zip (1..5).cycle{|a|puts a.join','}
# 45 characters, saves 3
(?a..?m).zip((1..5).cycle){|a|puts a.join','}
Değiştir if...else...end
ile üçlü operatörü ?:
. Bir dalda iki veya daha fazla ifade varsa, bunları parantez içine alın.
# 67 characters
if a<b
puts'statement 1'
puts'statement 2'else
puts'statement 3'end
# 62 characters, saves 5
a<b ?(puts'statement 1'
puts'statement 2'):(puts'statement 3')
Muhtemelen sahip değilsin while
ya da until
döngüler, ama yaparsan, onları değiştirici biçimde yaz.
(a+=1
b-=1)while a<b
puts'statement 3'
gerekli mi?
(Eğer pr gerektiğinden, bu dize interpolasyon kullanırken Martin BÜTTNER adlı kullanıcının yayınını ) nesne bir desen (olması durumunda, süslü parantez gerekmez $
, @
onun önünde). Gibi büyülü değişkenler için Faydalı $_
, $&
, $1
vb:
puts "this program has read #$. lines of input"
Bu nedenle, kullandığınızdan daha fazla bir değişken yazdırmanız gerekirse, bazı baytları kaydedebilirsiniz.
a=42; puts "here is a: #{a}"; puts "here is a again: #{a}"
$b=43; puts "here is b: #$b"; puts "here is b again: #$b"
Belirli bir öğenin e
aralık içinde olup olmadığını bulmanız gerekiyorsa r
,
r===e
daha uzun yerine:
r.cover?(e) # only works if `r.exclude_end?` is false
veya
r.member?(e)
veya
r.include?(e)
r===e
kısa değil mi?
===
uygulandı.
$_
son okuma satırı.
print
- yazdırılan içeriğin argümanı yoksa $_
~/regexp/
- için kısa $_=~/regexp/
Ruby 1.8'de, Kernel
bu konuda çalışan dört yöntem var $_
:
chop
chomp
sub
gsub
Ruby 1.9'da, bu dört yöntem yalnızca komut dosyanız -n
veya kullanıyorsa mevcuttur -p
.
Bazı değişkenleri sık sık yazdırmak istiyorsanız trace_var(:var_name){|a|p a}
trace_var
küresel $ değişkenleri ile çalışan tek
Dize enterpolasyonu kullanın!
Değiştirmek için to_s
. Bir dizgiye dönüştürmek istediğiniz her şeyin parantezine ihtiyacınız varsa, to_s
dize enterpolasyonundan iki byte daha uzundur:
(n+10**i).to_s
"#{n+10**i}"
Birleştirmeyi değiştirmek için. İki dize ile çevrili bir şeyi birleştirirseniz, enterpolasyon sizi bir bayttan kurtarabilir:
"foo"+c+"bar"
"foo#{c}bar"
Ayrıca, orta şeyin kendisi birleştirildiyse çalışır, yalnızca birleştirmeyi enterpolasyonun içine taşırsanız (birden çok enterpolasyon kullanmak yerine):
"foo"+c+d+e+"bar"
"foo#{c+d+e}bar"
length
içindeif a.length<n
length
6 bayt, kod golf biraz pahalı. Çoğu durumda, dizinin belirli bir noktada bir şey olup olmadığını kontrol edebilirsiniz. alacağınız son dizini geçerseniz nil
falsey değeri alırsınız .
Böylece değiştirebilirsiniz:
if a.length<5
için if !a[4]
-5 baytı
veya
if a.length>5
için if a[5]
-6 baytı
veya
if a.length<n
için if !a[n-1]
-3 baytı
veya
if a.length>n
için if a[n]
-6 baytı
Not : Sadece tüm gerçek değerleri içeren bir diziyle çalışacaktır. dizinin içinde nil
veya false
içinde olması sorunlara neden olabilir.
size
… Ama bu kesinlikle daha iyi. Btw, için String
de çalışıyor .
true
Ve false
anahtar kelimeleri kullanmayın .
kullanın:
!p
için true
(teşekkürler, histocrat!)!0
için false
. İhtiyacınız olan tek şey sahte bir değerse, o zaman kullanmanız yeterlidir p
(ki döner nil
).bazı karakterleri kaydetmek için.
true
(yani, eğer bir gerçeğe uygun değer yeterliyse, bir if koşulunda olduğu gibi), ihtiyacınız bile olmaz !!
.
p
(buna göre değerlendirilir nil
) daha kısa bir falsey değeridir. Hangi almak için en kısa yol demektir true
olduğunu !p
.
Dizileri a=i,*a
ters sırada almak için kullanarak oluşturun . Sıfırlamanıza bile gerek yok a
ve bunu yaparsanız bir dizi olması gerekmez .
Hiç bir numara almak gerekiyorsa ARGV
, get
veya benzer bir şey defalarca yerine çağıran bir şeyler yapmak to_i
üzerine, sadece kullanabilirsiniz ?1.upto x{do something x times}
x dizedir.
Böylece ?1.upto(a){}
yerine kullanarak x.to_i.times{}
2 karakter kaydedersiniz.
Ayrıca gibi şeyler yeniden yazabilir p 1 while 1
veya p 1 if 1
olarak p 1while 1
veyap 1if 1
Bu örnek pek kullanışlı değil, ancak başka şeyler için de kullanılabilir.
Ayrıca, bir dizinin ilk öğesini bir değişkene atamanız gerekirse, a,=c
iki karakterin karşısınaa=c[0]
Golf oyununuza yardımcı olacak yeni dil özelliklerinden haberdar olmak iyidir. En son Rubies'de birkaç tane harika olanlar var.
&.
Dönebilecek bir yöntem çağırdığınızda nil
ancak ek yöntem çağrıları zincirleme yapmak istiyorsanız, bu nil
durumu ele alan baytları boşa harcarsınız :
arr = ["zero", "one", "two"]
x = arr[5].size
# => NoMethodError: undefined method `size' for nil:NilClass
x = arr[5].size rescue 0
# => 0
"Güvenli gezinme operatörü", bir ifadenin tamamı için döner nil
ve dönerse , yöntem çağrıları zincirini durdurur nil
:
x = arr[5]&.size || 0
# => 0
Array#dig
& Hash#dig
Kısa bir ad ile iç içe geçmiş öğelere derin erişim:
o = { foo: [{ bar: ["baz", "qux"] }] }
o.dig(:foo, 0, :bar, 1) # => "qux"
İade nil
o çıkmaz vurursa:
o.dig(:foo, 99, :bar, 1) # => nil
Enumerable#grep_v
' Enumerable#grep
İn tersi , verilen argümana uymayan tüm öğeleri döndürür (karşılaştırıldığında ===
). Gibi grep
, bir blok verilirse bunun yerine sonuç döndürülür.
(1..10).grep_v 2..5 # => [1, 6, 7, 8, 9, 10]
(1..10).grep_v(2..5){|v|v*2} # => [2, 12, 14, 16, 18, 20]
Hash#to_proc
Verilen anahtar için değeri veren bir Proc döndürür, bu oldukça kullanışlı olabilir:
h = { N: 0, E: 1, S: 2, W: 3 }
%i[N N E S E S W].map(&h)
# => [0, 0, 1, 2, 1, 2, 3]
Ruby 2.4 henüz çıkmadı, ancak yakında olacak ve bazı harika küçük özelliklere sahip. (Yayınlandığı zaman, bu yazıyı dokümanlara bazı linklerle güncelleyeceğim.) Bu harika blog yayınında bunların çoğunu öğrendim .
Enumerable#sum
Daha fazla yok arr.reduce(:+)
. Şimdi sadece yapabilirsin arr.sum
. Numeric elements ( [].sum == 0
) için varsayılan olarak 0 olan, isteğe bağlı bir başlangıç değer bağımsız değişkenini alır . Diğer türler için bir başlangıç değeri sağlamanız gerekir. Ayrıca, eklemeden önce her bir elemana uygulanacak bir bloğu kabul eder:
[[1, 10], [2, 20], [3, 30]].sum {|a,b| a + b }
# => 66
Integer#digits
Bu, bir sayının basamağını en az en büyük önem sırasına göre döndürür:
123.digits # => [3, 2, 1]
Demek ki, 123.to_s.chars.map(&:to_i).reverse
bu oldukça hoş.
Bir bonus olarak, isteğe bağlı bir radix argümanını alır:
a = 0x7b.digits(16) # => [11, 7]
a.map{|d|"%x"%d} # => ["b", "7"]
Comparable#clamp
Teneke söylediklerini yapar:
v = 15
v.clamp(10, 20) # => 15
v.clamp(0, 10) # => 10
v.clamp(20, 30) # => 20
Karşılaştırılabilir olduğundan, Karşılaştırılabilir içeren herhangi bir sınıfla kullanabilirsiniz, örneğin:
?~.clamp(?A, ?Z) # => "Z"
String#unpack1
2 baytlık tasarruf .unpack(...)[0]
:
"👻💩".unpack(?U) # => [128123]
"👻💩".unpack(?U)[0] # => 128123
"👻💩".unpack1(?U) # => 128123
Numeric#ceil
, floor
vetruncate
Math::E.ceil(1) # => 2.8
Math::E.floor(1) # => 2.7
(-Math::E).truncate(1) # => -2.7
Bu Ruby'nin önceki sürümlerinde bir hataya neden olur, ancak 2.4'te izin verilir.
(a,b=1,2) ? "yes" : "no" # => "yes"
(a,b=nil) ? "yes" : "no" # => "no"
Math::E.ceil(1)
için Math::E.ceil 1
ve aynı şekilde için floor
ve truncate
.
Enumerable#sum
, .flatten.sum
2 bayttan daha kısa.sum{|a,b|a+b}
(-Math::E).truncate(1)
-Math::E.truncate(1)
1 bayt daha kısa olan eşdeğeri
&.
böyle bir abonelik ile kullanılabilir a&.[]i
(1 bayt daha kısa a&.at i
). Parantez gerekirse, a||a[i]
1 bayt a&.[](i)
veya daha kısa olsa daa&.at(i)
Bilimsel gösterim genellikle bir veya iki karakterin tıraş edilmesi için kullanılabilir:
x=1000
#versus
x=1e3
1e2
iyi 100.0
bir yüzdeye ihtiyaç duyduğundan daha iyidir .
1.0*
, 1 karakterden daha kısa.to_f
Demek istediğimi söyleyelim a*(b+c)
. Öncelik nedeniyle, a*b+c
işe yaramayacak (belli ki). Ruby'nin operatör olarak yöntemlerini kullanmanın en iyi yolu kurtarmaya geliyor! Sen kullanabilirsiniz a.*b+c
önceliğini yapmak *
daha düşüktür +
.
a*(b+c) # too long
a*b+c # wrong
a.*b+c # 1 byte saved!
Bu aynı zamanda !
ve ~
operatörleri ile de çalışabilir (unary +
ya da unary gibi şeyler -
işe yaramıyor, çünkü yöntemleri -@
ve +@
kaydediyor ()
ama ekliyorlar .@
)
(~x).to_s # too long
~x.to_s # error
x.~.to_s # 1 byte saved!
Kullanım ||
yerine or
ve &&
onun yerine and
.
Bir karakterin yanında, and
operatörün etrafındaki boşlukları (ve belki de braketi) kaydedebilirsiniz.
p true and false ? 'yes' :'no' #-> true (wrong result)
p (true and false) ? 'yes' :'no' #-> 'no'
p true&&false ? 'yes' :'no' #-> 'no', saved 5 characters
p true or false ? 'yes' :'no' #-> true (wrong result)
p (true or false) ? 'yes' :'no' #-> 'yes'
p true||false ? 'yes' :'no' #-> 'yes', saved 4 characters
Bir dizi üzerinde döngü yaparsanız normalde kullanın each
. Ancak map
bir dizi üzerinde de döngüler var ve bir karakterden daha kısa.
TDD kod-golf mücadelesine girmeye çalıştım. Özellikleri gibi bir şeydi
describe PigLatin do
describe '.translate' do
it 'translates "cat" to "atcay"' do
expect(PigLatin.translate('cat')).to eq('atcay')
end
# And similar examples for .translate
end
end
Code-golf uğruna, bir modül veya sınıf oluşturmak gerekmez.
Onun yerine
module PigLatin def self.translate s;'some code'end;end
biri yapabilir
def(PigLatin=p).translate s;'some code'end
13 karakter kaydeder!
PigLatin
, aynı zamanda etmek @pig_latin
, $pig_latin
ve 'pig'['latin']
.
translate
tanımlanmıştır nil
.
Çekirdek # p eğlenceli bir yöntemdir.
Yerine p var
kullanın puts var
. Bu, tamsayılar ve kayan noktalarla mükemmel çalışır, ancak her türle olmaz. Dizelerin etrafına tırnak işaretleri basıyor, bu muhtemelen istediğiniz şey değil.
Tek bir argümanla kullanıldığında, p
yazdırdıktan sonra argümanı döndürür.
Birden fazla argümanla kullanıldığında, p
bir dizideki argümanları döndürür.
Yerine p
(argüman olmadan) kullanın nil
.
p 'some string'
baskılar "some string"
ve sadece some string
diğerleri tarafından eleştirilmediği gibi.
p s
aynı puts s.inspect
, ama geri döners
#Each kullanmayın. #Map ile tüm öğelerin üzerinde döngü yapabilirsiniz. Yani yerine
ARGV.each{|x|puts x}
aynısını daha az baytta yapabilirsiniz.
ARGV.map{|x|puts x}
Tabii ki, bu durumda puts $*
daha kısa olurdu.
Rasyonel ve karmaşık sayılar için değişmezler vardır:
puts 3/11r == Rational(3,11)
puts 3.3r == Rational(66,20)
puts 1-1.i == Complex(1,-1)
=> true
true
true
Dizelerin içindeki çoğu baytı kullanabilirsiniz. "\x01"
(6 bayt), ""
(3 bayt) ile kısaltılabilir . Sadece bir bayt'a ihtiyacınız varsa, bu ?
(2 bayt) daha da kısaltılabilir .
Aynı şekilde, yeni satırları şöyle kısaltabilirsiniz:
(0..10).to_a.join'
'
=> "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10"
Sen kullanabilirsiniz ?\n
ve ?\t
bir byte daha kısa olan, hem de "\n"
ve "\t"
. Şaşırtmak için, ayrıca bir boşluk var.
Değiştirmeniz gerekse bile, argümanları dolaşmak yerine sabitleri kullanın. Tercüman, stderr'e uyarı verecek , fakat kimin umrunda. Birbirleriyle ilgili daha fazla değişken tanımlamanız gerekirse, bunları şu şekilde zincirleyebilirsiniz:
A=C+B=7+C=9
=> A=17, B=16, C=9
Bu C=9;B=16;A=17
ya da daha kısa C=0;B=C+7;A=C+B
.
Sonsuz bir döngüye ihtiyacınız varsa kullanın loop{...}
. Bilinmeyen uzunlukta ilmekler diğer ilmeklerle daha kısa olabilir:
loop{break if'
'==f(gets)}
while'
'!=f(gets);end
Biraz daha gsub / regexp püf noktaları. '\1'
Bir blok yerine özel kaçış karakterlerini kullanın:
"golf=great short=awesome".gsub(/(\w+)=(\w+)/,'(\1~>\2)')
"golf=great short=awesome".gsub(/(\w+)=(\w+)/){"(#{$1}~>#{$2})")
Ve $1
eğer işlem yapmanız gerekiyorsa özel değişkenler vb. Sadece bloğun içinde tanımlanmadıklarını unutmayın:
"A code-golf challenge." =~ /(\w+)-(\w+)/
p [$1,$2,$`,$']
=> ["code", "golf", "A ", " challenge."]
Boşluklardan, yeni satırlardan ve parantezlerden kurtulun. Yakut içinde biraz ihmal edebilirsiniz. Şüpheniz varsa, daima çalışıp çalışmadığını deneyin ve aklınızda bulundurun, bunun bazı editörlerin sözdizimini vurguladığını ...
x+=1if$*<<A==????::??==??
?\n
güzel, ama aslında tırnak içine newline karakter koymaktan daha kısa değil. (sekme için aynı)
puts$*
daha da kısa.
x+=1;$*<<A
Splat operatörünü kullanmanın başka bir yolu: tek bir dizi değişmez atamak istiyorsanız *
, sol taraftaki bir taraf sağ taraftaki köşebentlerden daha kısadır:
a=[0]
*a=0
Birden fazla değerle uyarıcı operatöre bile ihtiyacınız kalmaz (bu konuda beni düzelttiği için histocrat sayesinde):
a=[1,2]
a=1,2
Bir meydan okuma çıkış birden çok satır, yapmak zorunda kalmamasıdır gerektirdiğinde döngü mesela bir dizinin her satırı yazdırmak için sonuçları üzerinden. puts
Yöntem, bir dizi düzleştirmek ve ayrı bir satırda her öğe yazdırılır.
> a = %w(testing one two three)
> puts a
testing
one
two
three
Uyarıcı operatörünü #p
sizinle birleştirmek, onu daha da kısaltabilir:
p *a
Sıçrama operatörü (teknik olarak *@
yöntem, sanırım) ayrıca dizi olmayan numaralandırmanızı dizilere aktarır:
> p a.lazy.map{|x|x*2}
#<Enumerator::Lazy: #<Enumerator::Lazy: [1, 2, 3]>:map>
vs
> p *a.lazy.map{|x|x*2}
2
4
6
*@
bir yöntem değildir, uyarısı sözdizimsel şekerdir
a.uniq # before
a|[] # after
^^
[]
Bir değişkende boş bir dizi kullanacaksanız , daha fazla bayt kaydedebilirsiniz:
a.uniq;b=[] # before
a|b=[] # after
^^^^^
a&a
1 byte kısadır
Ruby'nin kısaltılmış bir versiyonu olan Ruby yerine Goruby kullanın. Rvm ile yükleyebilirsiniz
rvm install goruby
Goruby, kodunuzun çoğunu Ruby'de yazdığınız gibi yazmanıza izin verir, ancak yerleşik olarak ek kısaltmalar vardır. Bir şeyin mevcut en kısa kısaltmasını bulmak shortest_abbreviation
için, örneğin yardımcı yöntemini kullanabilirsiniz :
shortest_abbreviation :puts
#=> "pts"
Array.new.shortest_abbreviation :map
#=> "m"
String.new.shortest_abbreviation :capitalize
#=> "cp"
Array.new.shortest_abbreviation :join
#=> "j"
Ayrıca çok kullanışlı takma say
için puts
kendisi ile kısaltılmış edilebileceği s
. Yani yerine
puts [*?a..?z].map(&:capitalize).join
şimdi yazabilirsin
s [*?a..?z].m(&:cp).j
alfabeyi büyük harflerle basmak (bu çok iyi bir örnek değildir). Bu blog yazısı , daha fazla okumaya meraklıysanız, daha fazla şey ve içsel çalışmalardan bazılarını açıklar.
PS: h
yöntemi kaçırmayın ;-)
Bunu daha dün keşfettim. 's biti- pozisyonda n[i]
döndürür . Örnek:n
i
irb(main):001:0> n = 0b11010010
=> 210
irb(main):002:0> n[0]
=> 0
irb(main):003:0> n[1]
=> 1
irb(main):004:0> n[2]
=> 0
irb(main):005:0> n[3]
=> 0
irb(main):006:0> n[4]
=> 1
irb(main):007:0> n[5]
=> 0
n[0..3]
2 karakter kaydedebilir ve kullanabilirsiniz.
[*(...)]
onun yerine
(...).to_a
Örneğin, dizi olarak istediğimiz bir aralığımız olduğunu varsayalım:
(1..2000).to_a
Sadece böyle yap:
[*1..2000] # Parentheses around the (ran..ge) is not needed!
Ve şimdi bir dizi olarak menzile sahipsin.
[*1..2000]
çalışır?
Array#assoc
/rassoc
Bir dizi diziniz varsa ve belirli bir değerle başlayan alt diziyi bulmak istediğinizde Enumerable#find
, kullanmayın Array#assoc
:
a = [[0,"foo"],[0,"bar"],[1,"baz"],[0,"qux"]]
a.find{|x,|x==1} # => [1,"baz"]
a.assoc(1) # => [1,"baz"]
Bu aynı zamanda Enumerable#any?
bazı durumlarda iyi bir alternatiftir .
Array#rassoc
aynı şeyi yapar, ancak alt dizilerin son öğesini denetler:
a = [[123,"good"],[456,"good"]]
a.any?{|*,x|x=="bad"} # => false
a.rassoc("bad") # => nil
a.any?
hat rassoc
örneğin, ne yok |x,|
do? Nasıl farklı |x|
?
x=[1,2]
vs gibidir x,=[1,2]
. Yukarıdaki örneğimi kullanarak |x|
, ilk yinelemede x
olacak [0,"foo"]
. İle |x,y|
, x
olacak 0
ve y
olacak "foo"
. Aynı şekilde, birlikte |x,|
, x
olacaktır 0
. Başka bir deyişle, "ilk elemanı yerleştir x
ve gerisini
|,y|
, bir SyntaxError, ergo |_,y|
. Ama şimdi farkettim ki, |*,y|
eserler adında bir değişken kullanmaktan daha temiz _
(ama daha kısa değil).