簡體   English   中英

干燥此 ruby 代碼的最佳方法是什么?

[英]What is the best way to dry out this ruby code?

我想干掉這段代碼。 最好的解決方案是像在 rails 中使用 before_action 方法一樣嗎?

class Direction
    attr_accessor :dir

    def initialize(dir)
     @dir = dir
    end

    DIRECTIONS = %w[N E S W]

    def turn_left
     d = DIRECTIONS.find_index(dir)
     @dir = DIRECTIONS.rotate!(d-1).first
    end

    def turn_right
     d = DIRECTIONS.find_index(dir)
     @dir = DIRECTIONS.rotate!(d+1).first
    end
end
# frozen_string_literal: true

class Direction
  DIRECTIONS = %w[N E S W].freeze
  OPERATIONS = { left: :-, right: :+ }.freeze
  private_constant :DIRECTIONS, :OPERATIONS

  def initialize(dir)
    @dir = dir
  end

  OPERATIONS.keys.each do |turn_direction| # turn_left, turn_right 
    define_method "turn_#{turn_direction}" do
      turn(turn_direction)
    end
  end

  private

  attr_reader :dir

  def direction_index
    DIRECTIONS.find_index(dir)
  end

  def turn(operation)
    DIRECTIONS.rotate(direction_index.public_send(OPERATIONS[operation], 1)).first
  end
end

p Direction.new('N').turn_left  # "W"
p Direction.new('E').turn_left  # "N"
p Direction.new('S').turn_left  # "E"
p Direction.new('W').turn_left  # "S"
p Direction.new('N').turn_right # "E"
p Direction.new('E').turn_right # "S"
p Direction.new('S').turn_right # "W"
p Direction.new('W').turn_right # "N"

你可以:

  • freeze常量以避免修改。
  • 如果常量沒有在Direction class 之外使用,請更改它們的可見性。
  • 如果您僅在 class 本身中使用dir ,請更改它的可見性。
  • 創建一個OPERATIONS hash,它定義了方向和它將用於返回下一個方向的操作。
  • 遍歷OPERATIONS鍵以動態定義方法turn_leftturn_right
  • 定義一個direction_index方法,該方法使用dir返回DIRECTIONS中的索引。
  • 定義一個turn方法,它接收一個operation參數:
    • 使用operation ,您可以從OPERATIONS中獲取操作,它告訴如何旋轉(正或負)。
    • 將方法-+應用於direction_index的結果,您將獲得 arguments 旋轉。
    • 之后,您在DIRECTIONS上調用rotate並獲取第一個元素。

我建議使用哈希,主要是為了可讀性。

class Direction
  NEXT_LEFT  = { 'N'=>'W', 'W'=>'S', 'S'=>'E', 'E'=>'N' }
  NEXT_RIGHT = NEXT_LEFT.invert

  attr_reader :dir

  def initialize(dir)
    @dir = dir
  end

  def turn_left
    turn(NEXT_LEFT)
  end

  def turn_right
    turn(NEXT_RIGHT)
  end

  private

  def turn(nxt)
    @dir = nxt[@dir]
  end
end

d = Direction.new('N')
d.dir
  #=> "N" 
d.turn_left
  #=> "W" 
d.turn_left
  #=> "S" 
d.turn_right
  #=> "W" 

筆記:

NEXT_RIGHT
  #=> {"W"=>"N", "S"=>"W", "E"=>"S", "N"=>"E"}

很多好的答案,但一個簡單的直接解決方案是將兩種方法之間共有的部分分解為一個 turn 方法並傳入1-1

class Direction
  attr_accessor :dir

  def initialize(dir)
    @dir = dir
  end

  DIRECTIONS = %w[N E S W]

  def turn(delta_d)
    d = DIRECTIONS.find_index(dir)
    @dir = DIRECTIONS.rotate!(d + delta_d).first
  end

  def turn_left
    turn(-1)
  end

  def turn_right
    turn(1)
  end

end

您始終可以實現獨立於 state 的方向 map:

class DirectionMap
  def initialize(*list)
    # Create a Hash mapping table with left and right directions
    # pre-computed. This uses modulo to "wrap" the array around.
    @directions = list.map.with_index do |dir, i|
      [ dir, [ list[(i - 1) % list.length], list[(i + 1) % list.length] ] ]
    end.to_h
  end

  # These methods use dig to avoid blowing up on an invalid direction,
  # instead just returning nil for garbage input.
  def left(dir)
    @directions.dig(dir, 0)
  end

  def right(dir)
    @directions.dig(dir, 1)
  end
end

您現在可以在哪里導航任意指南針映射:

map = DirectionMap.new(*%w[ N E S W ])

map.left('N') # => 'W'
map.left(map.left('N')) # => 'S'

map.right('N') # => 'E'
map.right(map.left('N')) # => 'N'

所以你也可以做%w[ N NE E SE S SW W NW ]

我認為你可以避免每次轉動時創建新數組的所有工作(通過調用它來rotate )。 只需將當前方向存儲為數組中其字母的索引即可。 車削只是索引上的模運算(請注意,在 Ruby -1 % 4 == 3中)。 當你想要方向的字母時,只需使用索引從數組中獲取它。

class Direction
  DIRECTIONS = %w[N E S W].freeze

  def initialize(dir)
    self.dir = dir
  end

  # dir getter
  def dir
    DIRECTIONS[@dir_index]
  end

  # dir setter
  def dir=(dir)
    @dir_index = DIRECTIONS.index(dir)
  end

  # turning logic
  def turn(delta)
    @dir_index = (@dir_index + delta) % DIRECTIONS.size
    dir
  end

  def turn_left
    turn(-1)
  end

  def turn_right
    turn(1)
  end
end

p Direction.new('N').turn_left   #=> "W"
p Direction.new('E').turn_left   #=> "N"
p Direction.new('S').turn_left   #=> "E"
p Direction.new('W').turn_left   #=> "S"
p Direction.new('N').turn_right  #=> "E"
p Direction.new('E').turn_right  #=> "S"
p Direction.new('S').turn_right  #=> "W"
p Direction.new('W').turn_right  #=> "N"

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM