Які загальні поради щодо гольфу в Рубі?
Я шукаю ідеї, які можна застосувати для кодування проблем із гольфом загалом, характерних для Рубі. (Наприклад, "Видалити коментарі" не буде відповіддю.)
Будь ласка, опублікуйте одну пораду на відповідь.
Які загальні поради щодо гольфу в Рубі?
Я шукаю ідеї, які можна застосувати для кодування проблем із гольфом загалом, характерних для Рубі. (Наприклад, "Видалити коментарі" не буде відповіддю.)
Будь ласка, опублікуйте одну пораду на відповідь.
Відповіді:
?d
до ?~
1.8.$><<"string"
це коротше, ніж print"string"
.$<.map{|l|...}
коротше, ніж while l=gets;...;end
. Також ви можете використовувати, $<.read
щоб прочитати все це відразу.$<
і gets
читатимете його з файлу замість stdin, якщо ім'я файлу ввімкнено ARGV
. Таким чином, golfiest спосіб переопісать cat
буде: $><<$<.read
.cat
є залишити файл рубіну повністю порожнім (0 байт) і наполягати на тому, що його слід запускати з командного рядка з -p
прапором.
puts *$<
☺
або ♫
, або , якщо ви досить божевільний:?﷽.ord=65021
Використовуйте оператор splat, щоб отримати хвіст і голову масиву:
head, *tail = [1,2,3]
head => 1
tail => [2,3]
Це також працює в інший спосіб:
*head, tail = [1,2,3]
head => [1,2]
tail => 3
Використовуйте *
метод з рядком на масиві для приєднання елементів:
[1,2,3]*?,
=> "1,2,3"
abort
для завершення програми та надрукування рядка в STDERR - коротше, ніж puts
слідexit
gets
, ви можете використовувати його ~/$/
для пошуку довжини (це не враховує зворотний новий рядок, якщо він існує)[]
для перевірки, чи містить рядок інше:'foo'['f'] #=> 'f'
tr
замість gsub
заміни символів:'01011'.tr('01','AB') #=> 'ABABB'
chop
замістьchomp
abort
та~/$/
~/$/
gets
, його результат зберігається у $_
змінній. /regex/ ~= string
повертає індекс першого збігу. Виклик ~
у регулярному виразі еквівалентний /regex/ ~= $_
. Так це було б щось на зразокs=gets;l= ~/$/
end
.Спробуйте видалити end
зі свого коду.
Не використовуйте def...end
для визначення функцій. Зробіть лямбда з новим оператором -> в Ruby 1.9. (Оператор -> - "лямбда", або "ракета",. Це дозволяє економити 5 символів на функцію.
# 28 characters
def c n
/(\d)\1/=~n.to_s
end
# 23 characters, saves 5
c=->n{/(\d)\1/=~n.to_s}
Виклики методів є c n
або c(n)
. Дзвінки лямбда є c[n]
. Змінюючи один c n
на c[n]
витрати 1 символ, так що якщо ви можете використовувати c n
більш ніж в 5 разів, а потім зберегти цей метод.
Усі методи, які беруть do...end
блоки, можуть {...}
замість цього приймати блоки. Це економить від 3 до 5 символів. Якщо пріоритет {...}
занадто високий, використовуйте дужки, щоб виправити це.
# 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','}
Замініть if...else...end
на потрійного оператора ?:
. Якщо у відділення є два або більше висловлювань, загорніть їх у дужки.
# 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')
У вас, ймовірно, немає while
або until
циклів, але якщо ви це зробите, то запишіть їх у формі модифікатора.
(a+=1
b-=1)while a<b
puts'statement 3'
?
Коли ви використовуєте рядкову інтерполяцію (як слід додати публікацію Мартина Бюттнера ), вам не потрібні фігурні дужки, якщо на вашому об'єкті є сигіл ( $
, @
) перед ним. Корисно для магічних змінних , таких як $_
, $&
, і $1
т.д .:
puts "this program has read #$. lines of input"
Отже, якщо вам потрібно буде надрукувати змінну більше, ніж ви використовуєте її в іншому випадку, ви можете зберегти кілька байт.
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"
Якщо вам потрібно знайти, чи певний елемент e
знаходиться в межах діапазону r
, ви можете використовувати
r===e
замість довшого:
r.cover?(e) # only works if `r.exclude_end?` is false
або
r.member?(e)
або
r.include?(e)
r===e
коротше?
===
реалізовано.
$_
є останнім прочитаним рядком.
print
- якщо не вказано аргумент вмісту друку $_
~/regexp/
- короткий для $_=~/regexp/
У Ruby 1.8 у вас є чотири методи, Kernel
які працюють на $_
:
chop
chomp
sub
gsub
У Ruby 1.9 ці чотири методи існують лише у тому випадку, якщо ваш сценарій використовує -n
або -p
.
Якщо ви хочете часто друкувати якусь змінну, тоді використовуйте trace_var(:var_name){|a|p a}
trace_var
працює лише із глобальними змінними $
Використовуйте строкову інтерполяцію!
Для заміни to_s
. Якщо вам потрібні дужки навколо того, що ви хочете перетворити на рядок, to_s
це два байти довше інтерполяції рядків:
(n+10**i).to_s
"#{n+10**i}"
Щоб замінити конкатенацію. Якщо ви об'єднаєте щось, оточене двома іншими рядками, інтерполяція може врятувати вас один байт:
"foo"+c+"bar"
"foo#{c}bar"
Також працює, якщо середня річ сама є об'єднаною, якщо просто перемістити конкатенацію всередині інтерполяції (замість того, щоб використовувати кілька інтерполяцій):
"foo"+c+d+e+"bar"
"foo#{c+d+e}bar"
length
вif a.length<n
length
це 6 байт, трохи дорогий у коді гольф. у багатьох ситуаціях ви можете замість цього перевірити, чи є в масиві що-небудь в заданій точці. якщо ви перейдете останній індекс, який ви отримаєте nil
, значення фальси.
Отже ви можете змінити:
if a.length<5
до if !a[4]
-5 байт
або
if a.length>5
до if a[5]
-6 байт
або
if a.length<n
до if !a[n-1]
-3 байт
або
if a.length>n
до if a[n]
-6 байт
Примітка : працюватиме лише з масивом усіх правдоподібних значень. наявність масиву nil
або false
в ньому може спричинити проблеми.
size
… Але це, безумовно, краще. До речі, String
теж працює .
Не використовуйте ключові слова true
та false
.
Використання:
!p
за true
(дякую, гістократе!)!0
для false
. Якщо все, що вам потрібно, - це помилкове значення, то ви можете просто використовувати p
(що повертається nil
).щоб зберегти деякі символи.
true
(тобто, якщо триєтажних значень недостатньо, як, наприклад, в умовах if), вам навіть це не потрібно !!
.
p
(що оцінює nil
) - це коротше значення фальси. Що означає найкоротший шлях - true
це !p
.
Побудуйте масиви, використовуючи, a=i,*a
щоб отримати їх у зворотному порядку. Вам навіть не потрібно ініціалізуватися a
, і якщо ви це зробите, це не повинно бути масивом .
Якщо ви коли - небудь знадобиться , щоб отримати число від ARGV
, get
або що - щось подібне зробити що - то , що багато разів, замість виклику to_i
на нього, ви можете просто використовувати , ?1.upto x{do something x times}
де х є рядком.
Таким чином, використовуючи ?1.upto(a){}
замість, x.to_i.times{}
ви заощадите 2 символи.
Ви також можете переписати такі речі, як p 1 while 1
або p 1 if 1
як p 1while 1
абоp 1if 1
Цей приклад не дуже корисний, але його можна використовувати для інших речей.
Крім того, якщо вам потрібно призначити перший елемент масиву змінній, a,=c
збережете два символи на відміну відa=c[0]
Добре бути в курсі нових мовних особливостей, які допоможуть вашій грі в гольф. В останніх рубінах є кілька чудових.
&.
Коли ви викликаєте метод, який може повернутися, nil
але ви хочете пов'язати додаткові виклики методу, якщо це не так, ви витрачаєте байти, обробляючи цей nil
випадок:
arr = ["zero", "one", "two"]
x = arr[5].size
# => NoMethodError: undefined method `size' for nil:NilClass
x = arr[5].size rescue 0
# => 0
"Оператор безпечної навігації" зупиняє ланцюжок викликів методу, якщо повертається nil
та повертається nil
для цілого виразу:
x = arr[5]&.size || 0
# => 0
Array#dig
& Hash#dig
Глибокий доступ до вкладених елементів, з приємною короткою назвою:
o = { foo: [{ bar: ["baz", "qux"] }] }
o.dig(:foo, 0, :bar, 1) # => "qux"
Повертається, nil
якщо він потрапив у глухий кут:
o.dig(:foo, 99, :bar, 1) # => nil
Enumerable#grep_v
Зворотний - Enumerable#grep
повертає всі елементи, які не відповідають заданому аргументу (порівняно з ===
). Мовляв grep
, якщо вказано блок, його результат повертається замість цього.
(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
Повертає Proc, який дає значення для даного ключа, що може бути досить зручно:
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 ще не вийшов, але це скоро, і він має деякі чудові функції. (Коли вона вийде, я оновлю цю публікацію з деякими посиланнями на документи.) Про більшість із них я дізнався у цій чудовій публікації в блозі .
Enumerable#sum
Не більше arr.reduce(:+)
. Тепер ви можете просто зробити arr.sum
. Він приймає необов'язковий аргумент початкового значення, який за нульовими елементами ( [].sum == 0
) за замовчуванням дорівнює 0 . Для інших типів потрібно вказати початкове значення. Він також приймає блок, який буде застосовано до кожного елемента перед додаванням:
[[1, 10], [2, 20], [3, 30]].sum {|a,b| a + b }
# => 66
Integer#digits
Це повертає масив цифр числа у порядку найменшого значення:
123.digits # => [3, 2, 1]
У порівнянні, скажімо 123.to_s.chars.map(&:to_i).reverse
, це досить приємно.
Як бонус він приймає необов'язковий аргумент:
a = 0x7b.digits(16) # => [11, 7]
a.map{|d|"%x"%d} # => ["b", "7"]
Comparable#clamp
Робить те, що написано на бляшанці:
v = 15
v.clamp(10, 20) # => 15
v.clamp(0, 10) # => 10
v.clamp(20, 30) # => 20
Оскільки він є у Порівнянні, ви можете використовувати його з будь-яким класом, що включає Порівняльний, наприклад:
?~.clamp(?A, ?Z) # => "Z"
String#unpack1
2-байт економія понад .unpack(...)[0]
:
"👻💩".unpack(?U) # => [128123]
"👻💩".unpack(?U)[0] # => 128123
"👻💩".unpack1(?U) # => 128123
Numeric#ceil
, floor
іtruncate
Math::E.ceil(1) # => 2.8
Math::E.floor(1) # => 2.7
(-Math::E).truncate(1) # => -2.7
Це спричиняє помилку в попередніх версіях Ruby, але допускається в 2.4.
(a,b=1,2) ? "yes" : "no" # => "yes"
(a,b=nil) ? "yes" : "no" # => "no"
Math::E.ceil(1)
до Math::E.ceil 1
, а також для floor
і truncate
.
Enumerable#sum
, .flatten.sum
на 2 байти коротше, ніж.sum{|a,b|a+b}
(-Math::E).truncate(1)
еквівалентна на -Math::E.truncate(1)
1 байт коротше
&.
можна використовувати з такою підпискою a&.[]i
(на 1 байт коротше, ніж a&.at i
). Хоча, якщо потрібні дужки, a||a[i]
на 1 байт коротше a&.[](i)
абоa&.at(i)
Наукові позначення часто можна використовувати для гоління знаків або двох:
x=1000
#versus
x=1e3
1e2
краще, ніж 100.0
коли потрібен відсоток.
1.0*
на 1 char коротший, ніж.to_f
Скажімо, ви хочете висловитись a*(b+c)
. Через перевагу a*b+c
(очевидно) не вийде. Класний спосіб використання операторів Рубі на допомогу приходить на допомогу! Ви можете використовувати , a.*b+c
щоб зробити пріоритет з *
нижче , ніж у +
.
a*(b+c) # too long
a*b+c # wrong
a.*b+c # 1 byte saved!
Це може також працювати з !
і ~
операторами ( такими речами , як унарні +
або унарний -
не працює , тому що їх методи -@
і +@
, економлячи , ()
але додавання .@
)
(~x).to_s # too long
~x.to_s # error
x.~.to_s # 1 byte saved!
Використовуйте ||
замість цього or
і &&
замість цього and
.
Поруч із одним символом and
ви можете зберегти пробіли (а може бути і дужку) навколо оператора.
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
Якщо ви цикли на масиві, який ви зазвичай використовуєте each
. Але map
петлі також над масивом, і він на один символ коротший.
Я просто спробував TDD код-гольф виклик, тобто написати найкоротший код, щоб передати характеристики. Технічні характеристики були щось подібне
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
Заради коду-гольфу не потрібно створювати модуль чи клас.
Замість
module PigLatin def self.translate s;'some code'end;end
можна зробити
def(PigLatin=p).translate s;'some code'end
Зберігає 13 символів!
PigLatin
, а й @pig_latin
, $pig_latin
і 'pig'['latin']
.
translate
що було визначено на nil
.
Ядро # p - цікавий метод.
Використовуйте p var
замість puts var
. Це чудово працює з цілими числами та плавцями, але не з усіма типами. Він друкує лапки навколо рядків, що, мабуть, не те, що потрібно.
Використовується з одним аргументом, p
повертає аргумент після його друку.
Використовується з кількома аргументами, p
повертає аргументи в масив.
Використовуйте p
(без аргументів) замість nil
.
p 'some string'
друкує "some string"
і не тільки те, some string
що часто критикується іншими.
p s
те саме puts s.inspect
, але повертаєтьсяs
Не використовуйте #each. Ви можете просто перекинути всі елементи за допомогою #map. Так замість
ARGV.each{|x|puts x}
ви можете зробити те ж саме в менших байтах.
ARGV.map{|x|puts x}
Звичайно, у цьому випадку puts $*
було б ще коротше.
Існують букви для раціональних і складних чисел:
puts 3/11r == Rational(3,11)
puts 3.3r == Rational(66,20)
puts 1-1.i == Complex(1,-1)
=> true
true
true
Ви можете використовувати більшість байтів у рядках. "\x01"
(6 байт) можна скоротити до ""
(3 байти). Якщо вам потрібен лише цей один байт, це можна скоротити ще більше ?
(2 байти).
Таким же чином, ви можете отримати нові рядки, такі як:
(0..10).to_a.join'
'
=> "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10"
Ви можете використовувати ?\n
і те ?\t
, що на один байт коротше "\n"
і "\t"
. Для обтурації також є пробіл?
Використовуйте константи замість передачі аргументів навколо, навіть якщо вам потрібно їх змінити. Перекладач подаватиме попередження для більш жорсткого , але кого це не хвилює. Якщо вам потрібно визначити більше змінних, пов’язаних одна з одною, ви можете зв'язати їх так:
A=C+B=7+C=9
=> A=17, B=16, C=9
Це коротше C=9;B=16;A=17
або C=0;B=C+7;A=C+B
.
Якщо вам потрібна нескінченна петля, використовуйте loop{...}
. Петлі невідомої довжини можуть бути коротшими з іншими петлями:
loop{break if'
'==f(gets)}
while'
'!=f(gets);end
Ще кілька хитрощів gsub / regexp. Використовуйте спеціальні '\1'
символи втечі замість блоку:
"golf=great short=awesome".gsub(/(\w+)=(\w+)/,'(\1~>\2)')
"golf=great short=awesome".gsub(/(\w+)=(\w+)/){"(#{$1}~>#{$2})")
І спеціальні змінні $1
тощо, якщо вам потрібно виконати операції. Майте на увазі, що вони визначені не тільки всередині блоку:
"A code-golf challenge." =~ /(\w+)-(\w+)/
p [$1,$2,$`,$']
=> ["code", "golf", "A ", " challenge."]
Позбудьтесь пробілів, нових рядків та дужок. У рубіні можна зовсім трохи опустити. Якщо ви сумніваєтесь, завжди намагайтеся, чи не працює, і пам’ятайте, що це може порушити виділення синтаксису редактора ...
x+=1if$*<<A==????::??==??
?\n
приємно, але насправді не коротше, ніж насправді введення символів нового рядка в лапки. (те саме для вкладки)
puts$*
ще коротше.
x+=1;$*<<A
Ще один спосіб використання оператора splat: якщо ви хочете призначити один літеральний масив, a *
з лівого боку коротше дужок з правого боку:
a=[0]
*a=0
З кількома значеннями вам навіть не потрібен оператор splat (дякує гістократу за те, що мене виправляв):
a=[1,2]
a=1,2
Коли виклик вимагає вивести кілька рядків, вам не доведеться прокручувати результати, щоб надрукувати кожен рядок, наприклад, масив. puts
Метод буде вирівнюватися масив і роздрукувати кожен елемент в окремому рядку.
> a = %w(testing one two three)
> puts a
testing
one
two
three
Поєднавши оператора бризки, #p
ви можете зробити його ще коротшим:
p *a
Оператор splat (технічно це *@
метод, я думаю) також передає ваші масиви без масиву до масивів:
> p a.lazy.map{|x|x*2}
#<Enumerator::Lazy: #<Enumerator::Lazy: [1, 2, 3]>:map>
проти
> p *a.lazy.map{|x|x*2}
2
4
6
*@
не є методом, splat є синтаксичним цукром
a.uniq # before
a|[] # after
^^
Якщо ви будете використовувати порожній масив []
у змінній, ви можете зберегти ще більше байтів:
a.uniq;b=[] # before
a|b=[] # after
^^^^^
a&a
- на 1 байт коротше
Використовуйте Goruby замість Ruby, що є чимось на зразок скороченої версії Ruby. Ви можете встановити його за допомогою rvm via
rvm install goruby
Goruby дозволяє писати більшу частину коду так, як ви писали б Ruby, але вбудовані додаткові абревіатури. Щоб дізнатися про найкоротший доступний абревіатуру для чогось, ви можете скористатися допоміжним методом shortest_abbreviation
, наприклад:
shortest_abbreviation :puts
#=> "pts"
Array.new.shortest_abbreviation :map
#=> "m"
String.new.shortest_abbreviation :capitalize
#=> "cp"
Array.new.shortest_abbreviation :join
#=> "j"
Також дуже зручним є псевдонім, say
для puts
якого і сам його можна скоротити s
. Так замість
puts [*?a..?z].map(&:capitalize).join
тепер можна писати
s [*?a..?z].m(&:cp).j
друкувати алфавіт великими літерами (що не є хорошим прикладом). У цьому дописі в блозі пояснюється більше матеріалів та деяких внутрішніх робіт, якщо ви зацікавлені у подальшому читанні.
PS: не пропустіть h
метод ;-)
Я щойно відкрив це вчора. n[i]
повертає 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 символи та використовувати
[*(...)]
замість
(...).to_a
Наприклад, припустимо, у нас є діапазон, який ми хочемо як масив:
(1..2000).to_a
Просто роби це так:
[*1..2000] # Parentheses around the (ran..ge) is not needed!
А тепер у вас є масив як масив.
[*1..2000]
, теж працює?
Array#assoc
/rassoc
Коли у вас є масив масивів і ви хочете знайти підмасив, який починається з певного значення, не Enumerable#find
використовуйте Array#assoc
:
a = [[0,"foo"],[0,"bar"],[1,"baz"],[0,"qux"]]
a.find{|x,|x==1} # => [1,"baz"]
a.assoc(1) # => [1,"baz"]
Це також є хорошою заміною для Enumerable#any?
деяких ситуацій.
Array#rassoc
робить те ж саме, але перевіряє останній елемент підмасивів:
a = [[123,"good"],[456,"good"]]
a.any?{|*,x|x=="bad"} # => false
a.rassoc("bad") # => nil
a.any?
рядка в rassoc
прикладі, що |x,|
робити? Чим він відрізняється від |x|
?
x=[1,2]
проти x,=[1,2]
. Використовуючи мій приклад вище, з |x|
, в першій ітерації x
буде [0,"foo"]
. З |x,y|
, x
буде 0
і y
буде "foo"
. Так само, з |x,|
, x
буде 0
. Іншими словами, в ньому сказано: "покладіть перший елемент x
і відкиньте решту"
|,y|
, це SyntaxError, ergo |_,y|
. Але я тільки що зрозумів, що |*,y|
працює, що чистіше, ніж використання змінної з назвою _
(але не коротшої).