[英]Linked List written in ruby
I'm preparing for a technical interview and will be asked to write the algorithm for a linked list in ruby. 我正在准备进行技术面试,将被要求用ruby编写链表的算法。 I understand linked lists completely, but have struggled writing the code. 我完全了解链表,但是一直在努力编写代码。 Can someone show me how this is done? 有人可以告诉我这是怎么做的吗? I started it below.. 我从下面开始。
class Node
def initialize(item)
@item = item
@next = nil
end
end
You almost did it, really. 真的,您差不多做到了。 I can give you very old-school, Lisp-like implementation, if you are brave enough to show it to your interviewer. 如果您有足够的勇气向面试官展示,我可以给您一个非常古老的,类似于Lisp的实现。 In this approach, list is a pair (two elements touple), which first element contains the element, and second contains another pair, etc, etc. The last pair have nil
as a second element. 在这种方法中,list是一对(两个元素成对出现),第一个元素包含该元素,第二个元素包含另一对,依此类推,等等。最后一对具有nil
作为第二个元素。 And here is the whole implementation of the list in Ruby : 这是Ruby中列表的完整实现 :
class Pair
attr_reader :car, :cdr
def initialize(car, cdr=nil)
@car = car
@cdr = cdr
end
end
To construct the list, just use a lot of parenthesis, like in old, good Lisp: 要构建列表,只需使用很多括号,就像在旧的,好的Lisp中一样:
list = Pair.new(1, Pair.new(2, Pair.new(3)))
Now, the world is yours. 现在,世界就是你的。 You can do whatever you want with the list, using simple recursion. 您可以使用简单的递归对列表进行任何操作。 Here is an example of recursive inspect
: 这是递归inspect
的示例:
class Pair
def inspect
if cdr.nil?
car.inspect
else
"#{car.inspect}, #{cdr.inspect}"
end
end
end
pry(main)> list = Pair.new(1, Pair.new(2, Pair.new(3)))
=> 1, 2, 3
As you mentioned in a comment, you want to search the list. 正如您在评论中提到的那样,您想搜索列表。 Here is the code for this: 这是此代码:
class Pair
def find(index)
find_ index, 0
end
def find_(index, i)
if index == i
car
else
cdr.find_ index, i+1
end
end
end
pry(main)> list.find 2
=> 3
This is the standard Church Encoding of Lists (and Booleans): 这是列表(和布尔值)的标准教堂编码:
True = ->(iff, _) { iff }
False = ->(_, els) { els }
Pair = ->(first, rest) { -> x { x.(first, rest) }}
First = -> list { list.(True ) }
Rest = -> list { list.(False) }
List = Pair.(1, Pair.(2, nil))
First.(Rest.(List))
# => 2
It's not what you would actually write in Ruby, of course, but it is very simple and demonstrates an understanding of one of the most important principles of programming: code is data and data is code. 当然,这不是您实际上用Ruby编写的内容,但是它非常简单,并说明了对编程最重要原理之一的理解:代码就是数据,数据就是代码。
Here's a more realistic object-oriented encoding of lists: 这是列表的更现实的面向对象编码:
class List
include Enumerable
def self.[](*els) els.reverse_each.inject(Empty, &:cons) end
def cons(el) Pair[el, self] end
def prepend(prefix)
case
when empty? then prefix
when prefix.empty? then self
else prepend(prefix.rest).cons(prefix.first)
end
end
def to_s; "List[#{map(&:to_s).join(', ')}]" end
def inspect; "List[#{map(&:inspect).join(', ')}]" end
def each; return enum_for(__method__) unless block_given? end
class << Empty = new
def empty?; true end
alias_method :inspect, def to_s; 'Empty' end
freeze
end
Empty.freeze
class Pair < self
def initialize(first, rest=Empty)
self.first, self.rest = first, rest
freeze
end
def empty?; false end
def each(&blk)
return super unless block_given?
yield first
rest.each(&blk)
end
private
attr_writer :first, :rest
protected
attr_reader :first, :rest
class << self; alias_method :[], :new end
freeze
end
freeze
end
Note that there are absolutely no conditionals and no loops in the code. 请注意,代码中绝对没有条件,也没有循环。 That is always a good sign for object-oriented code: polymorphic method calls are more powerful than conditionals anyway, oftentimes, there simply is no need for conditionals. 对于面向对象的代码而言,这始终是一个好兆头:无论如何,多态方法调用比条件调用更强大,通常,根本不需要条件调用。
Some examples: 一些例子:
list1 = List::Pair[1, List::Pair[2, List::Pair[3, List::Empty]]]
# => List[1, 2, 3]
list2 = List::Empty.cons(6).cons(5).cons(4)
# => List[4, 5, 6]
list3 = List[7, 8, 9]
# => List[7, 8, 9]
list4 = list3.prepend(list2).prepend(list1)
# => List[1, 2, 3, 4, 5, 6, 7, 8, 9]
list4.partition(&:odd?)
# => [[1, 3, 5, 7, 9], [2, 4, 6, 8]]
Unfortunately, this object-oriented encoding will blow the stack for larger lists (on my system List[*(1..9338)].each {}
still works, but 9339
doesn't), even though each
is tail-calling itself and thus should run in O(1) stack space. 不幸的是,这种面向对象的编码会炸毁更大的列表(在我的系统中, List[*(1..9338)].each {}
仍然有效,但9339
无效),即使each
函数都自己调用因此应该在O(1)堆栈空间中运行。 As Guy L. Steele pointed out multiple times, OO languages must support proper tail calls, otherwise you are required to break OO in order to avoid blowing the stack. 正如盖伊·斯蒂尔(Guy L. Steele)多次指出的那样,面向对象的语言必须支持适当的尾部调用,否则,您必须中断面向对象,以避免破坏堆栈。 ( prepend
is not coded for tail-calls, but it can be rewritten that way.) ( prepend
代码未编码为尾部呼叫,但可以用这种方式重写。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.