Ruby 用 Ruby Koans 学习 Ruby,其中一道题,到 stackoverflow 上一看,高下立判啊!

neo · 2013年11月12日 · 最后由 error250 回复于 2016年04月13日 · 11927 次阅读

题目要求很简单,写一个 triangle(a,b,c) 函数,判断是等边,等腰,或一般三角形,并且能在有一边小于等于零或两边之和小于第三边的情况下 raise 一个 error 普通 Ruby 程序员:

def triangle(a, b, c)
  if a == 0 || b == 0 || c == 0
    raise TriangleError
  end
  if((a+b < c) || (a+c < b) || (b+c < a))
    raise TriangleError
  end
  if a == b && b == c # && a == c 
    :equilateral
  elsif a == b || b == c || c == a
    :isosceles
  else
    :scalene
  end
end

高端 Ruby 程序员:

def triangle(a, b, c)
  raise TriangleError if [a,b,c].min <= 0
  x, y, z = [a,b,c].sort
  raise TriangleError if x + y <= z
  [:equilateral,:isosceles,:scalene].fetch([a,b,c].uniq.size - 1)
end

再次验证了 Ruby 是一门优雅的语言啊。

👍 ,唉,个人尚未达到优雅模式

真简洁 👍

其实还能再剪掉一行...

triangle(A,B,C) ->
   [X,Y,Z] = lists:sort([A,B,C]),
   true = ((X+Y) > Z) and (X>0),
   case [X,Y,Z] of
       [X,X,X] -> equilateral;
       [X,X,Z] -> isosceles;
       [X,Y,Y] -> isosceles;
       [X,Y,Z] -> scalene
   end.
6楼 已删除

感觉最后一句缺乏可读性,使用 case 虽然会增加行数,但是更具可读性

@zj0713001 你的意思是这样?

def triangle(a, b, c)
  x, y, z = [a,b,c].sort
  raise TriangleError if [x,y,z].min <= 0 || x + y <= z
  [:equilateral,:isosceles,:scalene].fetch([a,b,c].uniq.size - 1)
end

就 3 个数比较,用?可读性还能接受的吧

def triangle(a, b, c)
  x, y, z = [a,b,c].sort
  raise TriangleError if x + y <= z || x <= 0
  x == z ? :equilateral : (x == y || y == z ? :isosceles : :scalene)
end

普通 Ruby 程序员写的代码的优点是,任何一个初级程序员或普通 Ruby 程序员都能读懂并明了代码的意图。

#8 楼 @shatle 太简洁了 ..

#8 楼 @shatle 我感觉这样会降低可读性吧,两个条件之间似乎没有关系。 #7 楼 @Teddy 最后一句还行吧,至少我看了会有 哦!的感觉

#9 楼 @quakewang 好像 ruby 的代码风格不推荐三目运算符嵌套三目的吧?

#7 楼 @Teddy #12 楼 @wcp1231 我也感觉最后一行没问题,而且我主要就是看到了这一行概括了那么多行所以才感到牛逼,你只要稍微有点编程基础应该都没有可读性问题。

#14 楼 @neo 那你读一遍,把声音录下来 😄

普通的写成这样也不过多了一行而已,还好读好跑

def triangle(a, b, c)
  raise TriangleError if a == 0 || b == 0 || c == 0
  raise TriangleError if (a + b < c) || (a + c < b) || (b + c < a)
  return :equilateral if a == b && b == c
  return :isosceles if a == b || b == c || c == a
  :scalene
end

#15 楼 @bhuztez 好冷。。。 😓

#16 楼 @reus 好吧,每个人感觉不一样,至少我看到这一大堆操作符的消失感觉很爽

高端的那个,创建了 4 个 array,min、sort、uniq 这些操作全都要遍历 array。 才 3 个元素可以不计较,但要是把这个当优雅,大的结构也这样用,那 vm 跑得再快也敌不过各种 O(n^x) x >= 2 的算法啊

#19 楼 @reus 你要说算法复杂度那就另算了,我本来就不是来说算法的,请不要钻牛角尖

#19 楼 @reus 主要是,判断里的这么多符号,多个 or 写的麻烦看的也麻烦。写错了也不好查吧。minsortuniq本身是为了简化对应的for的写法的吧,用在这也挺简洁的。 数据真要大到min之类的都成瓶颈了,就不是 ruby 能解决的了吧。

In elixir,

 triangle = fn [x,y,z] ->
   case Enum.sort [x,y,z] do
     [x,y,z] when (x+y<=z) or (x<0) -> raise "triangle error"
     [x,x,x] -> :equilateral
     [x,x,z] -> :isosceles
     [x,y,y] -> :isosceles
     [x,y,z] -> :scalene
   end
end

IO.puts triangle.([3,4,5])
IO.puts triangle.([1,1,9]) # => error

refs bhuztez

#8 楼 @shatle 耶 你答对了~ 但是楼上也有很多同学提出了意见 比方说 @reus 同学说的挺好哈 语法糖使用的时候还是要根据场景选择不一样的方式的

@zj0713001 ,其实我想说的是:我觉得还是原贴的好。

分开判断,避免可能不需要的其它操作;

通常,一个方法体内抛出的异常是不一样的;如果一样,尽量合在一块写。-+- 个人习惯

第一种是 java 程序员写出来的。

#13 楼 @neo 我见过一行代码里用 5 个三木运算符,看起来不是一般的别扭。

明明第一种是数学老师写的,写的这么 好,,初中生的作业题目而已,,大家想复杂了。。

#26 楼 @datty258 那是得多蛋疼= =

数体教... 三角形的三个不等式其实已经蕴含了三条边都大于 0 的结论.(假设我们有 a + b > ca + c > b , 两式子相加, 2a + b + c > b + c, 约简就有 a > 0).

另外 x == y 是可以重用的量哦.

def triangle a, b, c
  raise TriangleError if a + b <= c or a + c <= b or b + c <= a
  x, y, z = [a==b, b==c, c==a]
  return :equilateral if x and y
  return :isosceles if x or y or z
  :scalene
end

#29 楼 @luikore 不要这么绕好不好

#29 楼 @luikore 给数学帝跪了...

最后一行真心亮了,艺术性极强,让我想起编程是一门艺术

#31 楼 @jasl 初中不等式绝对有这题...

#30 楼 @bhuztez 那就更绕一点

cmp = [a==b, b==c, c==a]
return :e if cmp.inject :&
return :i if cmp.inject :|
return :s

#19 楼 @reus sort, uniq 都是 O(n log(n)) 啊, 10000 以下当 O(n) 一般不打紧...

优美的代码是需要代价的。。。。。。。。。。。。。。

补充:算法有错,旧帖保留是为了留着示众......

#5 楼 @bhuztez #35 楼 @luikore

简洁并且容易理解的方式应该是这样吧

triangle(X,Y,Z) ->
  case lists:usort([X,Y,Z]) of
    [0|_]                 -> error;
    [_A,_B]               -> isosceles;
    [_A]                  -> equilateral;
    [A,B,C] when C >= A+B -> error;
    _                     -> scalene
  end.

测试:

9> c(a).    
{ok,a}
10> a:test().
1 2 3 error
1 3 3 isosceles
3 2 3 isosceles
3 3 3 equilateral
0 2 3 error
ok

#38 楼 @fsword 😓你们写的已经越来越看不懂了。。

#35 楼 @luikore 不对啊,你这个代码对 [3,3,3] 和 [0,0,0] 处理结果显然是一样的啊

后续补充:有错误,易懂神马的就是浮云了

#39 楼 @wcp1231 真受伤,难道我写的不是最易懂的么

#38 楼 @fsword 原题要你抛错... 而且 1 1 3 的情况你也没考虑到

ὅπερ ἔδει δεῖξαι

#42 楼 @bhuztez 你说的对,我的算法思路有问题,应该在你的基础上改一下就行了

triangle(A,B,C) ->
  [X,Y,Z] = lists:sort([A,B,C]),
  true = ((X+Y) > Z) and (X>0),
  case lists:usort([X,Y,Z]) of
    [X] -> equilateral;
    [_A,_B] -> isosceles;
    _ -> scalene
  end.

测试用例补充

12> a:test().
1 2 3 error
1 3 3 isosceles
1 1 3 error
3 2 3 isosceles
3 3 3 equilateral
0 2 3 error
ok

#43 楼 @xds2000 要不要那么高端= =

#36 楼 @luikore 不是说 sort、uniq 是 O(n^2),前后没关系…

#44 楼 @fsword 这是什么语法?看不懂,望指教

#47 楼 @sefier elixir 貌似是一门新的语言

#47 楼 @sefier @kyktommy 用的是 elixir ,我和 @bhuztez 用的是 erlang,不过我一开始写错了

我还有待学习!

很漂亮的代码,看完确实有 的感觉,谢谢分享~

优雅的表达方式,更像人的思维习惯

确实看了自叹不如加醍醐灌顶啊 而且这句:[:equilateral,:isosceles,:scalene].fetch([a,b,c].uniq.size - 1) 我很惭愧的没有看懂

#53 楼 @zsxywz0928 饿。。。没看懂你是怎么个醍醐灌顶的= =

但是前面一个从复杂度上要更优。

#55 楼 @rasefon 是这样,后面要对数组做好几次遍历,但是这么小的数据量本来就无所谓,语法糖的使用反而能让代码看起来更赏心悦目。

但是现在如果要加入直角三角形和等腰直角三角形的判定…… 后一种就得重写了……朴实的 case 就可以很简单的加几行解决……

#57 楼 @Kabie 恩,是这样。但是如果是我来写这个三角形类的话我会另外写个方法来判断是否是直角,而不是写在这里面,这样应该更方便重用。

[:equilateral,:isosceles,:scalene].fetch([a,b,c].uniq.size - 1) 这句写的真好

def triangle(a, b, c): arr = [a,b,c] a, b, c = arr.sort() if a < 0 or a+b < c: raise Exception return ['equilateral','isosceles','scalene'][set(arr).size-1]

我认为这是高端 Ruby 中毒程序员才能写出的代码

这样改代码,会不会太难看?

def triangle(a, b, c)
    raise error if [a,b,c].min <= 0 or (a + b + c) <= 2 * [a,b,c].max
    [:equilateral,:isosceles,:scalene].fetch([a,b,c].uniq.size - 1)
end
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册