简体   繁体   English

用ruby编写的链表

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM