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:
?diç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 $<.readhepsini bir kerede okumak için kullanabilirsiniz .$<ve getseğer dosya adınız varsa stdin yerine bir dosyadan okuyacaksınız ARGV. Yani yeniden uygulamaya golfiest yolu catolacaktır: $><<$<.read.catda yakut dosyasını tamamen boş bırakmak (0 byte) ve -pbayrakla 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"
abortProgramı sonlandırmak ve standart hataya bir dize yazdırmak için - daha kısa putsizlediexitgets, 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'tryerine gsubkarakter akıllıca değiştirmeler için:'01011'.tr('01','AB') #=> 'ABABB'chopyerinechompabortve~/$/
~/$/
gets, sonucu $_değişkende saklanır . /regex/ ~= stringilk 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.endKodunuzdan 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 nveya c(n). Lambda aramaları c[n]. Her birinin maliyeti 1 karakter olarak değiştirildiği c niçin c[n], c n5 kereden fazla kullanabiliyorsanız , yöntemi saklayın.
do...endBlokları 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...endile üç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 whileya da untildö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ı $_, $&, $1vb:
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 earalı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===ekı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, Kernelbu konuda çalışan dört yöntem var $_:
chopchompsubgsubRuby 1.9'da, bu dört yöntem yalnızca komut dosyanız -nveya kullanıyorsa mevcuttur -p.
Bazı değişkenleri sık sık yazdırmak istiyorsanız trace_var(:var_name){|a|p a}
trace_varkü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_sdize 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"
lengthiçindeif a.length<nlength6 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 nilfalsey değeri alırsınız .
Böylece değiştirebilirsiniz:
if a.length<5için if !a[4]-5 baytı
veya
if a.length>5için if a[5]-6 baytı
veya
if a.length<niçin if !a[n-1]-3 baytı
veya
if a.length>niçin if a[n]-6 baytı
Not : Sadece tüm gerçek değerleri içeren bir diziyle çalışacaktır. dizinin içinde nilveya falseiçinde olması sorunlara neden olabilir.
size… Ama bu kesinlikle daha iyi. Btw, için Stringde çalışıyor .
trueVe falseanahtar kelimeleri kullanmayın .
kullanın:
!piçin true(teşekkürler, histocrat!)!0iç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 trueolduğunu !p.
Dizileri a=i,*aters sırada almak için kullanarak oluşturun . Sıfırlamanıza bile gerek yok ave bunu yaparsanız bir dizi olması gerekmez .
Hiç bir numara almak gerekiyorsa ARGV, getveya 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 1veya p 1 if 1olarak p 1while 1veyap 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,=ciki 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 nilancak ek yöntem çağrıları zincirleme yapmak istiyorsanız, bu nildurumu 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 nilve dönerse , yöntem çağrıları zincirini durdurur nil:
x = arr[5]&.size || 0
# => 0
Array#dig & Hash#digKı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 nilo çı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_procVerilen 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#sumDaha 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#digitsBu, 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).reversebu 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#clampTeneke 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#unpack12 baytlık tasarruf .unpack(...)[0]:
"👻💩".unpack(?U) # => [128123]
"👻💩".unpack(?U)[0] # => 128123
"👻💩".unpack1(?U) # => 128123
Numeric#ceil, floorvetruncateMath::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 1ve aynı şekilde için floorve truncate.
Enumerable#sum, .flatten.sum2 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
1e2iyi 100.0bir 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+ciş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 orve &&onun yerine and.
Bir karakterin yanında, andoperatö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 mapbir 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_latinve 'pig'['latin'].
translatetanımlanmıştır nil.
Çekirdek # p eğlenceli bir yöntemdir.
Yerine p varkullanı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, pyazdırdıktan sonra argümanı döndürür.
Birden fazla argümanla kullanıldığında, pbir 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 stringdiğerleri tarafından eleştirilmediği gibi.
p saynı 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 ?\nve ?\tbir 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=17ya 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 $1eğ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==????::??==??
?\ngü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. putsYö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ü #psizinle 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&a1 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_abbreviationiç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 sayiçin putskendisi 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: hyöntemi kaçırmayın ;-)
Bunu daha dün keşfettim. 's biti- pozisyonda n[i]döndürür . Örnek:ni
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/rassocBir 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 xolacak [0,"foo"]. İle |x,y|, xolacak 0ve yolacak "foo". Aynı şekilde, birlikte |x,|, xolacaktır 0. Başka bir deyişle, "ilk elemanı yerleştir xve 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).