Les astuces ultimes pour faire du golfing en Ruby
Les bases
Retrouvez notre page dédiées aux bases du golfing en Ruby ici →
Récup l’input
Utiliser dd
plutôt que gets
peut permettre de gagner un espace quand il n’y a qu’un seul input (équivalent à $stdin.read
ou $<.read
)
puts`dd`
# vs
puts gets
n=eval`dd`
# vs
n=gets.to_i
On peut aussi utiliser sed
pour ignorer la première ligne
puts`sed 1d`
# vs
gets;puts $<.read
On peut mapper toute l’entrée en même temps avec $<
n,a,b=$<.map &:to_i
# vs
n=gets.to_i
a=gets.to_i
b=gets.to_i
Utiliser l’étoile pour utiliser directement $<
a,b,c=*$<
# vs
a,b,c=$<.map{_1}
a,b,c=$<.to_a
Faire un gets peut permettre de supprimer le premier élément de $<
gets;d=$<.map &:chars
# vs
d=$<.map(&:chars)[1..]
$_
stocke la dernière ligne lue avec gets
, on peut donc ne pas l’assigner
gets;puts (1..10).map{$_.to_i*_1}
# vs
n=gets.to_i;puts (1..10).map{n*_1}
Comment print
Utiliser p
pour print les entiers et les booléens
p 1
# vs
puts 1
Utiliser p *
pour print des tableaux d’entiers ou de booléens
a=[1,2,3]
p *a
# vs
puts a
Utiliser $><<
pour print sans retour à la ligne
$><<a
# vs
print a
# !! attention à la priorité de l'opérateur <<
$><<(a?'a':'b')
# vs
print a?'a':'b'
Les tableaux
Les crochets peuvent être omis pour l’initialisation d’un tableau de plus de 2 éléments
d=1,2
# vs
d=[1,2]
Pour initialiser un tableau d’un seul élément, on peut utiliser l’opérateur étoile
*d=1
# vs
d=[1]
Utiliser map
pour itérer sur un tableau (Attention si des perf sont nécessaires)
a.map{}
# vs
a.each{}
Utiliser [*..]
pour convertir les ranges en tableaux plutot que la méthode .to_a
d=*1..10
# vs
d=(1..10).to_a
.product
permet de générer toutes les combinaisons possibles d’un tableau
d=1,2,3
d.product d
# => [[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1], [3, 2], [3, 3]]
Utiliser l’opérateur *
plutôt que .join
a=1,2,3
puts a*" "
# vs
puts a.join" "
puts a*""
# vs
puts a.join
.each_cons
permet de générer tous les sous-tableaux de n éléments adjacents
d=*1..10
d.each_cons(3).to_a
# => [[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6],
# [5, 6, 7], [6, 7, 8], [7, 8, 9], [8, 9, 10]]
.each_slice
permet de découper le tableau en sous-tableaux de taille n
d=*1..10
d.each_slice(3).to_a
# => [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]
Utiliser l’évaluation plutôt qu’un .reduce
sur les opérations simples
d=1,2,3,4
p eval d*?*
# vs
p d.reduce :*
Valeurs uniques dans un tableau
[0,1,1,3,3,3,2]|[] # => [0, 1, 3, 2]
# vs
[0,1,1,3,3,3,2].uniq # => [0, 1, 3, 2]
a=0,1,1,3,3,3,2
a&a
# vs
a|[]
# vs
a.uniq
tally ?!? Compte les occurrences de chaque élément d’un Enumerable
%w'a b d a b a a a z'.tally
# vs
%w'a b d a b a a a z'.reduce(Hash.new 0){_1[_2]+=1;_1}
%w'a b d a b a a a z'.group_by{_1}.map{[_1,_2.size]}.to_h
%w'a b d a b a a a z'.group_by{_1}.transform_values &:size
# => {"a"=>5, "b"=>2, "d"=>1, "z"=>1}
Transposition d’une matrice
m=[1,2],[3,4],[5,6],[7,8]
m.transpose
# vs
...very long
# => [[1, 3, 5, 7], [2, 4, 6, 8]]
Utiliser (a...b)
plutot que (a..b-1)
flat_map vs flatten puis map
String
Initialiser un string d’un seul caractère
c=?c
# vs
c='c'
Utiliser .split
sans arguments
"Hello, world!".split # => ["Hello,", "world!"]
# vs
"Hello, world!".split(' ') => ["Hello,", "world!"]
Attention cependant à quelques subtilités de .split
"a b\nc d".split # => ["a", "b", "c", "d"]
# vs
"a b\nc d".split(' ') # => ["a", "b", "c", "d"]
# vs
"a b\nc d".split(/ /) # => ["a", "b\nc", "d"]
.bytes
permet de travailler directement sur les valeurs ascii
"abcdefghi".bytes
# vs
"abcdefghi".chars.map(&:ord)
Utiliser des regex est souvent plus court que des méthodes empiriques
"AbCdEfGhIjKlMnOpQrStUvWxYz".scan /[A-Z]/
# vs
"AbCdEfGhIjKlMnOpQrStUvWxYz".chars.select{_1==_1.upcase}
%w()
permet d’initialiser un tableau de plusieurs string
%w(one two three)
# vs
["one","two","three"]
.next
permet d’incrémenter les caractères alphanumériques d’une string (surtout utile pour les heures)
"12:24".succ # => "12:25"
.tr
permet de traduire une liste de caractère en une autre
"hello, world!".tr("lo","10") # => "he110, w0r1d"
.tr
est plus court que .delete
pour supprimer les caractères
"hello, world!".tr("lo","") # => "he, wrd"
# vs
"hello, world!".delete("lo") # => "he, wrd"
On peut tester si une string inclut une substring avec l’opérateur []
string["substring"] # renvoie "substring" ou nil
# vs
string.include?('substring')
On peut se servir du même opérateur pour réaliser un sub
string["substring"]='sub'
# vs
string.sub("substring",'sub')
Positions des lettres dans l’alphabet
a=?a.ord # 97
A=?A.ord # 65
p a-97 # => 0
p A-65 # => 0
p a%32 # => 1
p A%32 # => 1
On peut passer par un tableau pour concaténer des nombres
a=1
b=2
[a,b]*' '
# vs
"#{a} #{b}"
a.to_s+" "+b.to_s
Autres
Atteindre les limites de l’interpréteur ruby !
a=2
a<2?3:5 # => 5
# vs
2>a?3:5 # => Syntax error
# vs
2>a ?3:5 # => 5
a=2
a>1?:OK:'Not OK' # => OK
# vs
a<2?'Not OK'::OK # => Syntax error
# vs
a<2?'Not OK':'OK' # => OK
p$.
# vs
p $.
'abcd'.tr'a-d','1-4'
# vs
'abcd'.tr('a-d','1-4')
Lorsque les valeurs possibles sont limitées, on peut souvent remplacer une égalité par une inégalité
a%10<1
# vs
a%10==0
.digits
permet de générer le tableau des chiffres en base quelconque
13.digits # => [3,1]
57.digits 9 # => [3,6]
Certaines variables sont définie dans ruby, il est possible des les utiliser afin d’éviter de déclarer une nouvelle variable
# print diagonal of input
$<.map{$><<_1[$.-1]}
# vs
i=-1;$<.map{$><<_1[i+=1]}
Utiliser le spaceship operator <=>
pour indexer un tableau
puts %w"a_égal_b a_plus_grand_que_b a_plus_petit_que_b"[a<=>b]
puts ["a égal b","a plus grand que b","a plus petit que b"][a<=>b]
# vs
puts a<b ?"a plus petit que b":a>b ?"a plus grand que b":"a égal b"
De la même manière, on peut utiliser des formules en index de tableau plutot que des conditions
a=gets.to_i
a*[3,5][a%2]
# vs
a*(a%2<1?3:5)
(?1..?9)
pour ne pas avoir à faire des .to_s
dans le range
# count number of each digits in given string
s=gets
(?0..?9).map{s.count _1}
# vs
(0..9).map{s.count _1.to_s}
L’opérateur %
sur un range permet d’ajouter un step
d=*(1..9)%2
# vs
d=*1.step(9,2)
# vs
d=(1..9).filter{_1%2>0}
# => [1, 3, 5, 7, 9]
Les captures de la dernière regex est stockée dans la variable $~ on peut accéder aux éléments $~[1]
directement par $1
/ $~[2]
par $2
, etc… $~[0]
est le match complet
On peut utiliser l’implémentation des nombres négatifs et la priorité des opérateurs unaires pour gagner des caractères (plus d’info sur le complément à 2)
~-a == a-1
-~a == a+1
On peut assigner plusieurs variables en même temps en chaînant les assignations
a=b=c=d=0
# vs
a=0
b=0
c=0
d=0
permutation = combien de combinaison et ordres différents tu peux faire
combination = combien de combinaison différente tu peux faire
https://codegolf.stackexchange.com/questions/363/tips-for-golfing-in-ruby