这么一段代码涉及了很多 ruby 的知识点。
尤其是在 initialize 中直接接受 block 参数太漂亮了,想想如果要实现不同的排序方法,只需要将这个方法生成一个 Proc,传递给数组就可以了。
class SortedArray < Array
def initialize(*args,&sort_by)
@sort_by = sort_by || Proc.new { |x,y| x <=> y }
super(*args)
sort! &@sort_by
end
def insert(i,v)
insert_before = index(find {|x| @sort_by.call(x,v) == 1})
super(insert_before ? insert_before : -1, v)
end
def <<(v)
insert(0,v)
end
alias push <<
alias unshift <<
["collect!","flatten!","[]="].each do |method|
self.class_eval %{
def #{method}(*args)
super
sort! &@sort_by
end
}
end
end
这个效率很差啊,每次都要重新 sort,Array 还有很多口子没处理 concat 啊,delete_if 啊等等。先 find 遍历,再用 index 效率也很低,不在乎重复元素直接用 SortedSet 还好点。
创作如绘画,在能表达主题的基础之上,还讲究一笔就勾出神韵,画面减无可减,当中没有任何多余的东西。简洁,而不简陋。
代码的简洁,是在不影响阅读的基础上,用最简单直接的方式实现,每一句话,每一个字符都是必须的(这个角度看,ruby 中不应该用括号)。《代码整洁之道》和《编写可读代码的艺术》关于这方面都有很系统的总结。
这段代码真的很简洁,变量的取名也不错,应该优化了好几个版本吧!大家读下来都知道作用是什么,而且能一口气读完。
除了简洁之外,美的代码还会注意排版的美观,很在意对齐,我觉得这段代码还可以更美的。
忍不住用我的理解的方式改了一下:
class SortedArray < Array
def initialize( *args, &sort_by )
@sort_by = sort_by || Proc.new { |x,y| x <=> y }
super *args
sort_self!
end
def insert( i, v )
next_neighbour = find { |x| @sort_by.call(x,v) > 0 }
insert_before = index( next_neighbour ) || -1
super insert_before, v
end
def <<( v )
insert 0, v
end
alias push <<
alias unshift <<
# 其实加入concat这样的入口非常方便,在这个数组里面添加就是了
methods_to_wrap = %w[
collect!
flatten!
[]=
concat
]
methods_to_wrap.each do |method|
# 这里我尝试用 define_method 代替 class_eval
# 这样阅读代码的时候,能有更好的语法高亮。
# 但是 define_method 不支持 *args 这样的动态
# 参数,只好放弃了。保持 class_eval
class_eval %{
def #{method}(*args)
super
sort_self!
end
}
end
private
# 加入这个private method 是为了DRY,很多地方都用到了
def sort_self!
sort! &@sort_by
end
end
至于性能优化,是另外的话题,至少 insert 方法有优化空间
:)