在Ruby中打高尔夫球的技巧


62

您可以为Ruby打高尔夫球提供哪些一般技巧?

我正在寻找可用于编码一般针对Ruby的高尔夫问题的想法。(例如,“删除评论”将不是答案。)

请为每个答案发布一个提示。


有人需要编写一种名为Rub的语言,该语言对每个Ruby令牌都使用一个Unicode字符,有点像Jelly和Pyth :)
Mark Thomas

Answers:


46
  • 100至126可被写为数字?d?~ 1.8。
  • 同样,如果您需要1.9中的单字符字符串,则x小于“ x”。
  • 如果您需要打印字符串而不附加换行符,$><<"string"则比print"string"
  • 如果您需要阅读多行输入,$<.map{|l|...}则比短while l=gets;...;end。你也可以用$<.read它一次阅读所有内容。
  • 如果您应该从文件中读取文件,$<并且gets文件名位于中,则将从文件中读取文件而不是stdin文件ARGV。因此,要重新实现golfiest方式cat是:$><<$<.read

1
?x通常会产生ascii代码,因此您可以实际地将所有可打印内容转换为两个字符的数字。1.9有所不同,“ a” .ord产生ascii数字,但比十进制版本长4个字节。
Hiato 2011年

8
一种更容易实现的方法cat是将ruby文件完全空白(0字节),并坚持应从带有-p标志的命令行运行它。
daniero

1
或者,通过@daniero自己的答案puts *$<
不是说查尔斯(Charles)

1
因此,在1.8中,我要做的只是去~~它将返回126?
Simply Beautiful Art

5
您可以使用诸如或的想法来超越126 ,或者如果您足够疯狂的话:?﷽.ord=65021
《美丽的艺术》

32

使用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"

27
  • 使用abort终止程序和打印字符串STDERR -短于puts后跟exit
  • 如果您阅读带有的行gets,则可以使用~/$/来查找其长度(如果存在,则不算结尾的换行符)
  • 使用[]检查一个字符串包含另一个:'foo'['f'] #=> 'f'
  • 使用tr代替进行gsub按字符替换:'01011'.tr('01','AB') #=> 'ABABB'
  • 如果您需要删除结尾的换行符,请使用chop代替chomp

2
+1 abort~/$/
J -_- L

请说明如何使用~/$/
Mathieu CAROFF

每次调用@MathieuCAROFF时gets,其结果都存储在$_变量中。 /regex/ ~= string返回第一个匹配项的索引。调用~正则表达式等效于/regex/ ~= $_。所以就像s=gets;l= ~/$/
Cyoce

20

结束你的end

尝试end从您的代码中删除。

不要def...end用来定义函数。在Ruby 1.9中使用new->运算符创建一个lambda。(->运算符是“ stabby lambda”(即“破折号火箭”)。)每个函数可节省5个字符。

# 28 characters
def c n
/(\d)\1/=~n.to_s
end

# 23 characters, saves 5
c=->n{/(\d)\1/=~n.to_s}

方法调用是c nc(n)。Lambda电话是c[n]。将每个字符更改c nc[n]1个字符,因此,如果可以使用c n5次以上,请保留该方法。

所有采用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')

您可能没有whileuntil循环,但如果有或循环,则以修饰符形式编写它们。

(a+=1
b-=1)while a<b

圆括号是否puts'statement 3'必要?
Cyoce '16

15

除w0lf

使用数组时,.compact可以替换-[nil]为保存2个字符。

与上面的组合->您可以使其更短-[p]以节省另外2个字符。


14

使用短预定义变量只要有可能,例如$*代替ARGV。有他们的好名单在这里,有很多其他有用的信息。


12

当您使用字符串插值(如你应该公关马丁布特内尔的帖子),你不需要花括号,如果你的对象有一个印记($@在它的前面)。此功能用于对神奇的变量$_$&$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"

11

如果您需要查找某个特定元素e是否在范围内r,则可以使用

r===e

而不是更长的时间:

r.cover?(e) # only works if `r.exclude_end?` is false

要么

r.member?(e)

要么

r.include?(e)

3
不会r===e更短吗?
阿库恩2012年

@akuhn是的。矮得多。感谢您指出这一点,它帮助我将代码缩短了10个字符,这是巨大的:codegolf.stackexchange.com/a/6125/3527
Cristian Lupascu 2012年

1
别客气。switch语句中可以使用的所有内容均已===实现。
akuhn 2012年

10

$_ 是最后读取的行。

  • print -如果没有参数给出打印内容 $_
  • ~/regexp/ - 短缺 $_=~/regexp/

在Ruby 1.8,你有四种方法Kernel,其操作上$_

  • chop
  • chomp
  • sub
  • gsub

在Ruby 1.9中,仅当您的脚本使用-n或时,这四种方法才存在。-p

如果您想经常打印一些变量,请使用 trace_var(:var_name){|a|p a}


2
这些仅在使用-p-n选项运行Ruby时可用。 参考。
达伦·斯通

1
似乎trace_var仅适用于全局$变量
daniero

10

使用字符串插值!

  1. 更换to_s。如果您想在任何要变成字符串的地方加上括号,to_s则比字符串插值要长两个字节:

    (n+10**i).to_s
    "#{n+10**i}"
    
  2. 替换串联。如果将其他两个字符串包围的内容串联起来,则插值可以为您节省一个字节:

    "foo"+c+"bar"
    "foo#{c}bar"
    

    如果中间的东西本身是串联的,那么也可以使用,如果您只是将串联在插值内移动(而不是使用多个插值):

    "foo"+c+d+e+"bar"
    "foo#{c+d+e}bar"
    

10

避免 lengthif a.length<n

length是6个字节,在代码高尔夫中有点昂贵。在许多情况下,您可以检查数组在给定点是否有任何东西。如果您超过最后一个索引,您将获得nil假值。

因此,您可以更改:

if a.length<5if !a[4] -5个字节

要么

if a.length>5if a[5] -6个字节

要么

if a.length<nif !a[n-1] -3个字节

要么

if a.length>nif a[n] -6个字节

注意:仅适用于所有真实值的数组。在阵列中nilfalse阵列中可能会引起问题。


4
我一直使用size…但这绝对更好。顺便说一句,也适用String
manatwork

10

不要使用truefalse关键字。

采用:

  • !ptrue(谢谢,历史学家!)
  • !0false。如果您需要的只是一个伪造的值,那么您可以简单地使用p(返回nil)。

保存一些字符。


1
除非您实际需要true(即,如果真值足够,如在if条件下),您甚至不需要!!
马丁·恩德

4
并且类似地,p(计算为nil)是较短的假值。这意味着最短的获取方法true!p
histocrat

@histocrat好点!我已经编辑了答案。
克里斯蒂安·卢帕斯库


9

如果您需要从ARGVget或类似的方法中获取一个数字以进行多次操作,则无需调用to_i它,只需使用?1.upto x{do something x times} x是字符串即可。

所以用?1.upto(a){}代替x.to_i.times{}您节省2个字符。

您也可以像p 1 while 1p 1 if 1那样重写p 1while 1p 1if 1

该示例不是很有用,但可以用于其他用途。

另外,如果您需要将数组的第一个元素分配给变量,a,=c则会保存两个字符,而不是a=c[0]


9

Ruby 2.3和2.4的新功能

最好与新的语言功能保持同步,这将有助于您的高尔夫比赛。最新的红宝石有一些很棒的。

Ruby 2.3

安全导航操作员: &.

当您调用一个可能返回的方法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#digHash#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]

红宝石2.4

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

由于它在Comparable中,因此您可以将其与任何包含Comparable的类一起使用,例如:

?~.clamp(?A, ?Z) # => "Z"

String#unpack1

节省了2个字节.unpack(...)[0]

"👻💩".unpack(?U)    # => [128123]
"👻💩".unpack(?U)[0] # => 128123
"👻💩".unpack1(?U)   # => 128123

精密论据Numeric#ceilfloortruncate

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 1floortruncate
Simply Beautiful Art

1
@SimplyBeautifulArt我希望在Ruby中打高尔夫球的人能够自己实现飞跃。
乔丹

对于Enumerable#sum.flatten.sum.sum{|a,b|a+b}
Asone Tuhid

(-Math::E).truncate(1)等效于-Math::E.truncate(1)缩短1个字节
Asone Tuhid

1
&.可以与这样的下标一起使用a&.[]i(比短1个字节a&.at i)。虽然,如果需要括号,a||a[i]则比a&.[](i)或短1个字节a&.at(i)
Asone Tuhid,

7

科学记数法通常可用于去除一两个字符:

x=1000
#versus
x=1e3

9
注意:这将返回Float值(1000.0)而不是Integer,这可能会导致使用大数字导致结果不准确。
Dogbert

4
啊,1e2100.0需要一定百分比更好。
Phrogz

类似于此原理,1.0*.to_f
-Unihedron

7

使用运算符方法代替括号

假设您要表达a*(b+c)。由于优先级,a*b+c将无法工作(显然)。Ruby将运算符作为方法的很酷的方法来了!您可以使用a.*b+c设置*低于的优先级+

a*(b+c) # too long
a*b+c   # wrong
a.*b+c  # 1 byte saved!

这也可以与!and ~运算符一起使用(一元+或一元之类的东西-不起作用,因为它们的方法是-@and +@,保存()但添加.@

(~x).to_s # too long
~x.to_s   # error
x.~.to_s  # 1 byte saved!

6

使用||替代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也会在数组上循环,它要短一个字符。


6

我只是尝试了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个字符!


7
哈,很彻底。你不仅添加必要的行为PigLatin,而且要@pig_latin$pig_latin'pig'['latin']
histocrat 2014年

@histocrat:现在我明白了。这是因为translate已在上定义了nil
Eric Duminil

6

内核#p是一种有趣的方法。

使用p var代替puts var。这非常适合整数和浮点数,但不适用于所有类型。它在字符串周围打印引号,这可能不是您想要的。

与单个参数一起使用,p在打印后返回参数。

与多个参数一起使用,p以数组形式返回参数。

使用p(不带参数)代替nil


10
不幸的是,并非只有别人经常批评的p 'some string'版画。"some string"some string
Patrick Oscity

1
基本上p s与相同puts s.inspect,但是返回s
Cyoce '16

6

不要使用#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"。对于混淆,还存在一个空格。


即使您需要更改常量,也请使用常量而不是传递参数。口译员将警告stderr,但谁在乎。如果需要定义更多彼此相关的变量,可以像这样链接它们:

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$*更短。
Cyoce

我知道您想证明一个观点,但是我很确定最后一个示例与x+=1;$*<<A
Asone Tuhid

6

使用splat运算符的另一种方法:如果要分配单个数组文字,*则左侧的a 短于右侧的方括号:

a=[0]
*a=0

使用多个值,您甚至不需要splat运算符(感谢histocrat对此进行了纠正):

a=[1,2]
a=1,2

后一种情况实际上不需要splat。
histocrat

@histocrat哇,我认为在这种情况下,第二个值将被丢弃。
马丁·恩德

1
我不敢相信我在Ruby中打高尔夫球的所有时间都不知道这些。
Doorknob

6

当一个挑战,需要你输出多行,你不必循环通过你的结果,以便打印的每一行如数组。该puts方法将展平数组并将每个元素打印在单独的行上。

> a = %w(testing one two three)
> puts a
testing
one
two
three

将splat运算符与#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

1
*@不是方法,splat是语法糖
Asone Tuhid

6

删除数组的重复元素时节省一些字节

a.uniq # before
a|[]   # after
    ^^

如果将[]在变量中使用空数组,则可以保存更多字节:

a.uniq;b=[] # before
a|b=[]      # after
      ^^^^^

2
对于第一种情况,a&a短1字节
Asone Tuhid

5

使用Goruby代替Ruby,后者类似于Ruby的缩写版本。您可以通过rvm安装它

rvm install goruby

Goruby允许您像编写Ruby一样编写大多数代码,但是内置了其他缩写。要找出某事物的最短缩写,可以使用helper方法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为也非常方便。所以代替putss

puts [*?a..?z].map(&:capitalize).join

你现在可以写

s [*?a..?z].m(&:cp).j

用大写字母打印字母表(这不是一个很好的例子)。此博客文章如果您有兴趣进一步阅读介绍了更多内容和一些内部工作原理。

PS:不要错过h方法;-)


超过2年后,我终于想通了什么这个答案让我想起了 ...
undergroundmonorail

5

要加入一个数组,而不是这个

[...].join

做这个

[...]*''

节省2个字节。要与分隔符一起使用

[...]*?,

5

下标数字!

我昨天才发现这个。在第- 个位置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]
Simply Beautiful Art

4

您可能可以保存2个字符并使用

[*(...)]

代替

(...).to_a

例如,假设我们有一个想要作为数组的范围:

(1..2000).to_a

像这样做:

[*1..2000]  #  Parentheses around the (ran..ge) is not needed!

现在,您可以将范围作为数组。


5
我也认为[*1..2000]有效吗?
林恩


4

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|有何不同?
Cyoce

@Cyoce块参数解构与解构赋值遵循相同的规则,所以就像x=[1,2]vs 一样x,=[1,2]|x|在上面的示例中,使用,在第一次迭代x中将使用[0,"foo"]。与|x,y|x0y将会"foo"。同样,with |x,|x将是0。换句话说,它说:“把第一个元素放进去,x然后把其余的扔掉
Jordan

注意,它不能反向工作,例如|,y|SyntaxError,ergo |_,y|。但是我现在才意识到它|*,y|可以工作,这比使用名为_(但不能更短)的变量更干净。
约旦
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.