Які загальні поради щодо гольфу в Рубі?
Я шукаю ідеї, які можна застосувати для кодування проблем із гольфом загалом, характерних для Рубі. (Наприклад, "Видалити коментарі" не буде відповіддю.)
Будь ласка, опублікуйте одну пораду на відповідь.
Які загальні поради щодо гольфу в Рубі?
Я шукаю ідеї, які можна застосувати для кодування проблем із гольфом загалом, характерних для Рубі. (Наприклад, "Видалити коментарі" не буде відповіддю.)
Будь ласка, опублікуйте одну пораду на відповідь.
Відповіді:
?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слідexitgets, ви можете використовувати його ~/$/для пошуку довжини (це не враховує зворотний новий рядок, якщо він існує)[]для перевірки, чи містить рядок інше:'foo'['f'] #=> 'f'trзамість gsubзаміни символів:'01011'.tr('01','AB') #=> 'ABABB'chopзамістьchompabortта~/$/
~/$/
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які працюють на $_:
chopchompsubgsubУ 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<nlengthце 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#unpack12-байт економія понад .unpack(...)[0]:
"👻💩".unpack(?U) # => [128123]
"👻💩".unpack(?U)[0] # => 128123
"👻💩".unpack1(?U) # => 128123
Numeric#ceil, floorіtruncateMath::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|працює, що чистіше, ніж використання змінної з назвою _(але не коротшої).