Help us understand the problem. What is going on with this article?

Re: Ruby で季節のような循環する概念を表したい

More than 1 year has passed since last update.

この記事はOkinawa.rb Advent Calendar 2018の2日目の記事です。
昨日は @hanachin_ さんのRailsのviewでPDFを書くSATySFi-rails gemの使い方でした。
明日は @hanachin_ さんのRe: シクシク素数アドベントカレンダー Ruby 編です。

こちらの記事を読んで色々書きたくなったので書きます。
https://qiita.com/QUANON/items/5beef387a4b2941aa239

双方向循環リスト

たぶん書けるといいのはこれ(ただし要素数が変更されることはない)

双方向循環リスト(doubly-circularly-linked list)では、各ノードは線形の双方向リストと同じように2つのリンクを持つが、先頭ノードの後方リンクは最後尾ノードを指し、最後尾ノードの前方リンクは先頭ノードを指す。双方向リストと同様、挿入も削除もその位置に隣接するノードへの参照が1つあれば、高速に行える。構造的には双方向循環リストには先頭も最後尾もないが、一般に外部のアクセスポインタを用意して、先頭または最後尾のノードを指しておくことが多い。そして、双方向リストでの番兵ノードのように順序を把握するのに使われる[1]。
https://ja.wikipedia.org/wiki/%E9%80%A3%E7%B5%90%E3%83%AA%E3%82%B9%E3%83%88#%E5%8F%8C%E6%96%B9%E5%90%91%E5%BE%AA%E7%92%B0%E3%83%AA%E3%82%B9%E3%83%88

片方向循環リスト

コメント欄で書いたこれは実質片方向循環リストっぽい動きで、ノードを辿りながら1つずつ取り出すみたいなのができる(seasonがポインタ的になっていてnextするとseasonが上書きされる)

season = %w[春 夏 秋 冬].cycle
season.next # => "春"
season.next # => "夏"
season.next # => "秋"
season.next # => "冬"

双方向循環リスト

Arrayのリスト自体を回す発想 (seasonがポインタでrotateすると状態が変更される)

season = %w[春 夏 秋 冬].extend Module.new {
  def next
    first
  ensure
    rotate!
  end

  def prev
    rotate!(-1)
    last
  end
}
season.next # => "春"
season.next # => "夏"
season.next # => "秋"
season.next # => "冬"
season.prev # => "秋"
season.prev # => "夏"
season.prev # => "春"

扱うEnumerableが無限の場合

考えたくないのでこの実装はなし

扱うのが春夏秋冬の場合

数が少なかったらベタに書いて動きそう

class Node < Struct.new(:value, :prev, :next)
  def next
    super.call
  end

  def prev
    super.call
  end

  alias to_s value
end

, , ,  = Node.new("春", -> {  }, -> {  }), Node.new("夏", -> {  }, -> {  }), Node.new("秋", -> {  }, -> {  }), Node.new("冬", -> {  }, -> {  })

season = 
7.times do
  puts season.to_s
  season = season.next
end

8.times do
  puts season.to_s
  season = season.prev
end
# 春
# 夏
# 秋
# 冬
# 春
# 夏
# 秋
# 冬
# 秋
# 夏
# 春
# 冬
# 秋
# 夏
# 春

めちゃくちゃでかい配列でどう書けばいいんか

雑にnextとprevを直で定義

seasons = %w[
  立春 雨水 啓蟄 春分 清明 穀雨
  立夏 小満 芒種 夏至 小暑 大暑
  立秋 処暑 白露 秋分 寒露 霜降
  立冬 小雪 大雪 冬至 小寒 大寒
]

seasons.inject(seasons.last) do |prev_season, season|
  season.define_singleton_method(:prev) do
    prev_season
  end
  season
end

seasons.reverse_each.inject(seasons.last) do |next_season, season|
  season.define_singleton_method(:next) do
    next_season
  end
  season
end

season = seasons.first

seasons.size.times do
  puts season
  season = season.next
end

seasons.size.times do
  season = season.prev
  puts season
end

経路が定義されていればいい

経路を定義しておいてStringにRefinementsでメソッドを足して感じにした

seasons = %w[
  立春 雨水 啓蟄 春分 清明 穀雨
  立夏 小満 芒種 夏至 小暑 大暑
  立秋 処暑 白露 秋分 寒露 霜降
  立冬 小雪 大雪 冬至 小寒 大寒
]

using Module.new {
  prev_path = {}
  seasons.inject(seasons.last) do |prev_season, season|
    prev_path[season] = prev_season
    season
  end

  next_path = {}
  seasons.reverse_each.inject(seasons.last) do |next_season, season|
    next_path[season] = next_season
    season
  end

  refine(String) do
    define_method(:prev) do
      prev_path.fetch(self)
    end

    define_method(:next) do
      next_path.fetch(self)
    end
  end
}

season = seasons.first

seasons.size.times do
  puts season
  season = season.next
end

seasons.size.times do
  season = season.prev
  puts season
end

まとめ

Rubyべんり

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away