soukouki’s diary

誰かの役に立つ記事をかけたらいいなあ

信号パターンをいくつか紹介・解説するよ!

 この記事は、Simutrans Advent Calendar 2019の6日目の記事です。 adventar.org

 OTRP v22.3(Standerd版120.4.1相当)で動作を確認しています。信号については概ねStanderd版と同じ動作ですが、多閉塞信号は動作が異なります。

はじめに

 ある場所で配線自慢をしてたら、思ったより反応があったので、アドベントカレンダーの記事にしてみました。アドベントカレンダー初挑戦なので、暖かく見守っていただければありがたいです。

 また、この記事では信号についてのチュートリアルは書いていません。基本的な信号の動きについては、チュートリアル/信号入門/102.2.2 - Simutrans日本語化・解説がおすすめです。

 なお、混乱を防ぐため、信号機普通信号と、どの種類の信号(または駅)でも良い場所は、信号と表記します。また、スクリーンショット内では表記を短くするためプライオリティシグナルではなく三現示信号と表記します。

信号機について

 先ほど紹介したページに書かれていない仕様や、アドオンで追加できる信号もあるので、それについて紹介します。

ATC信号

 とても小さくて目立たない信号機です。この記事では本来の使い方とは異なり、変な置き方をする信号をごまかすときに使っています。

両方向信号は、同時に両方向から予約できない

 A地点にいる列車がD方向に向けて経路を予約しています。その際、信号が設置されているCのマスも予約します。これは反対側から走らせたときも同じです。 f:id:soukouki:20191121233426p:plain

 これによって、両方向から列車を走らせた際、信号のあるマスは片方側からしか予約できず、両方向信号でぶつかることはありません。この仕様は、単線での続行運転をするときなどに役立ちます。

入線振分信号は他の信号機を超えて予約する

 A地点にいる列車がC駅に向けて進んでいます。このとき、Aの入線振分信号を通過する際に、Bの普通信号の先のC駅まで予約します。 f:id:soukouki:20191123140913p:plain

 Cの普通信号を増やしたり、 f:id:soukouki:20191123141047p:plain

プレシグナルやプライオリティシグナルに置き換えても同じようにその先まで予約します。 f:id:soukouki:20191123141247p:plain

ですが、入線振分信号の場合は、その手前で止まります。 f:id:soukouki:20191123141404p:plain

 なお、反対向きの信号や一方通行標識によるルートの制限はきちんと働きます。この仕様のおかげで成り立つパターンもあります(ただ、今回の記事では紹介しません)。

OTRPv22.3などで多閉塞信号の予約範囲が先の信号を超えることがある

 A駅にいる列車がC駅に向かって進んでいます。多閉塞信号を通る際、C駅の端に置かれた普通信号を超えて予約しています。 f:id:soukouki:20191204230219p:plain

 この先、いくつか多閉塞信号を使うパターンを紹介しますが、それを使う場合は列車の予約範囲に気を使う必要があります。

 また、先の信号によって動きが異なるときがありますが、それについては詳しくは扱いません。

f:id:soukouki:20191205212147p:plain
多閉塞信号の先の信号は、左から普通×2、プレシグナル×2、多閉塞×2、振分×2、プライオリティシグナル×2です。

信号・配線パターン紹介

 信号の話が長くなってしまいましたが、ここからが本題です。

停車位置を指定したときに、出発時に出発信号まで進まないようにする方法

 OTRPでは、ホームの指定の位置に列車を停められます。

 A駅に列車が停まろうとしています。この駅では駅舎に近いところに列車を停めるようにしています。 f:id:soukouki:20191124230123p:plain

 普通なら、下のスクリーンショットのように反対側からの列車が来る前に出発して、信号まで進んでから止まります。ですが、このままでは見た目が良くないので、駅を出発してもホームに停まったままにするパターンを紹介します。 f:id:soukouki:20191124230356p:plain

 線路の色が違うところが停車位置を設定したマスです。 f:id:soukouki:20191124231519p:plain

 列車は、後ろ側が入るのであれば停車位置を設定したマスに、それだけでは足りない場合は、最後尾の車両が入るように指定より先のマスに、ホーム長よりも長ければホームの一番先に停まります。

 なので、それに合うようにプレシグナルを置いていきます。そうすると、発車した際にプレシグナルに引っかかり、普通信号が開通するまでは先の方へ行かなくなります。

 なお、プレシグナルをこのように使うとかなりゴツくなってしまうので、ATC信号を使ってごまかすことをおすすめします。 f:id:soukouki:20191124231737p:plain

 ただし、駅を通過する列車がいる場合、最も手前側のプレシグナルで止まってしまうので、注意が必要です。

プライオリティシグナルを使った急行退避の確率を上げる信号パターン

 B駅に停まっている列車を、A地点にいる通過列車が追い越そうとしています。また、A地点にいる列車は駅に侵入する前から、B駅より先の区間まで予約しています。 f:id:soukouki:20191123104842p:plain

 そして、この信号配置では、B駅にいる列車が先に発車してしまった場合、通過列車はC地点までの区間を予約します。 f:id:soukouki:20191123105406p:plain

 プレシグナルや普通信号だけを使った信号パターンでは、追い越しに失敗した場合、通過列車がA地点で止まってしまい、先行列車との間隔が開いてしまいます。それに対して、プライオリティシグナルでは合流部手前まで進むことができるので、次の追い越しが成功しやすくなったり、運行間隔を詰められたりします。

多閉塞信号を使った緩急接続の確率を上げる信号パターン

 なお、このパターンはStanderd版ではうまく動きません。

 B駅に止まっている列車を、A地点にいる急行列車がB駅に停まり、そして追い越していきます。SSの中に通常信号って書いてあるのは気のせいです。きっと。直すのめんどい。 f:id:soukouki:20191123114049p:plain

 鈍行列車はAの多閉塞信号を通るときに、C:待避線の普通信号までを予約、急行列車はB駅を超えて、Dの信号まで予約します。そのため、鈍行列車が急行列車より先にB駅を出発しても、急行列車がDの信号を過ぎるまでの間、C:待避線の普通信号から先を予約できないので、追い越しが成功します。

 ただし、急行列車がAの多閉塞信号から先を予約する前に鈍行列車が発車した場合、急行列車はAの多閉塞信号から先を予約できないので、列車の間隔が開いてしまいます。

 また、OTRPv22.3などでは多閉塞信号の仕様に気をつけないといけません。

その他追い越し・緩急接続の確率を上げるパターン

 いくつか併用することもあります。

鈍行列車に待機時間(積むまで待機・最大待ち時間)を設定する

 一番簡単で、上のSSでも使用しています。ただし、鈍行列車が連続して来たときに詰まったり、空退避が起こることがあります。

退避側の出発信号をプレシグナルにする

 追い越しや、追い越しと緩急接続を同時に行う駅で使うことがあります。緩急接続をしながら追い越しも起こる場合、順番に発車します。ただし、ある程度運行本数が多くないと、うまく働きません。

複線からの単線分岐

 ここでは、両方向信号を使い、少ない設備で複線側の支障時間を減らしたパターンを紹介します。

f:id:soukouki:20191123142029p:plain

 なお、E方面は駅や編成が複数あった場合もうまく動きます。

列車の動き

  • 本線側を通る列車は、AのプレシグナルでCの信号までを予約します。予約に失敗した場合、A地点の前で待機します。

  • 支線側に行く列車は、AのプレシグナルでDの両方向普通信号を超え、E方向の駅や信号所まで予約します。予約に失敗した場合、A地点で支線の列車が出ていくまで待機します。

  • そしてE方向の駅について折り返す(E方面の信号所を出発する)際は、Dの両方向普通信号まで区間を予約します。この際、本線側は予約しないので、E方向の駅が相当離れていても、本線側が詰まったりすることはありません。

  • Dの両方向普通信号に着くと、Cの信号まで予約をします。予約に失敗した場合はD地点で待機します。

その他

 このパターンのメリットは、とにかく設備を減らせることです。田舎感を出せるかも!

 ちなみに支線側に行く列車が1編成だけであれば、ただ単に両方向普通信号をこれと同じ場所に置くだけで実現できます。また、Bの普通信号はATC信号に置き換えたほうがスッキリして好きです。

単線での続行運転

 シンプルに両方向普通信号を真ん中においたものです。前に説明したように、両方向信号は両方から同時に予約できないので、これだけでうまくいきます。 f:id:soukouki:20191123152703p:plain

 ただし、両方向にある程度列車が貯められるようにしないと、デッドロックが起こります。特に、単線区間の長さに比べて高頻度に運転している場合は、片方向にのみ列車が流れやすく、デッドロックが起こる可能性が高まります。

単線での急行運転

 地方の特急列車とか、再現してみたくないですか?

 ただし、単線のため信号を置く場所が限られたり、急行列車と鈍行列車の間隔を詰められず、駅間が長いほど鈍行列車が長時間駅に停車するようになるなど、なかなか難易度が高いです。

 また、設備を減らしていくと、どうしても無理が出てしまい、デッドロックする可能性がある配線がいくつもあります。使用する区間や運転頻度にも気を配らないといけません。

 なお、実際に作り、確認するようにはしていますが、これを使って開発したことはないので、間違いがあるかもしれません。

2面4線-急行同士・鈍行同士のすれ違い

 急行停車駅にも、通過駅にも使いやすい2面4線駅です。ただ、単線から4線になるのはリアルではあまり見ないので、景観的には微妙かもしれません。 f:id:soukouki:20191204212751p:plain

 急行通過駅のときは、前後の駅にプライオリティシグナルやプレシグナルを使うことで、追い越しの確率をあげられます。

 きれいに追い越しが決まるようにするには、急行が駅の先も予約するようにします。たとえば、

  • 急行が通過の場合
    1. 前の駅(信号場)の出発信号をプライオリティシグナルにする。
    2. 通過線に信号を設置しない。
    3. 前の駅の出発信号をプレシグナルに置き換える。

     2と3は、急行がこの駅を超えて予約するまでは前の駅にとどまり続けます。線路あたりの輸送効率は落ちますが、運転停車を避ける意味ではありだと思います。

  • 急行が停車の場合
     急行用ホームに信号を設置せず、前の駅の出発信号を多閉塞信号に置き換えます。
     急行はこの駅を超えて予約するまでの間、前の駅にとどまり続けます。

2面3線

 急行だけがすれ違うのか、鈍行だけがすれ違うのか。急行は通過なのか停車なのか。など色々バリエーションが考えられ、またそれぞれ信号の置き方も変わってきます。

  1. 片方向のみ急行退避(通過)の場合
    上の2面4線の半分と、普通の単線を並べるだけです。信号については、ただ並べただけなのでそのまま設置するだけです。

  2. 鈍行のみすれ違い
     急行が通過なのか、停車なのかで変わります。

    • 急行が通過の場合
       信号は一切置かなくていいです。景観的に必要であれば、鈍行用ホームには普通信号を置いても構いません。
       鈍行が退避すると前の駅にいた急行がこの駅の先まで予約し、追い越しが成功します。
       また、急行用ホームに信号を置き、急行を単線での続行運転のようにさせる手もあります(ただ、単線での続行運転と同じくデッドロックする可能性があります)。

    • 急行が停車の場合
       急行用ホームには信号を置かず、鈍行用ホームには普通信号を置きます。
       急行は上で紹介した単線での続行運転のような形になるので、衝突することはありません(これも、単線での続行運転と同じくデッドロックする可能性があります)。
       ただし、急行がすれ違う設備を間に入れずに、この形の駅を連続で置くとデッドロックが起こります。解決する方法(多閉塞信号)もありますが、この記事内では紹介しません。

  3. 急行のみすれ違い
     鈍行用ホームは信号を置かず、急行用ホームは普通のすれ違い駅(信号場)を並べます。
     この形の駅はすれ違う設備を間に入れずに連続で置くと、デッドロックが起こりますし、それを解決する方法は私は知りません(多閉塞信号を使うと追い抜けなくなります)。特急街道プレイには1のパターンを使うのが無難だと思います。

1面2線-一線スルー

 なかなかな田舎感が出そうです。

 信号は一切置きません。鈍行列車は単線続行のように、急行列車はただの単線として走ります。 f:id:soukouki:20191205213130p:plain

 この形の駅は、すれ違う設備を間に入れずに連続で置くと、デッドロックが起こりますし、それを解決する方法は私は知りません(多閉塞信号を使うと追い抜けなくなります)。非一線スルーや2面4線などの鈍行列車がすれ違えるパターンを間に挟む必要があります(単線での続行運転と同じくデッドロックする可能性があります)。

1面2線-非一線スルー

 普通の単線のすれ違いと変わりません。 f:id:soukouki:20191205213204p:plain

おわりに

 少し長くなってしまいましたが、これを読んでくださった方が信号に興味を持ち、大和西大寺駅のような、複雑な配線・信号のあるマップを作ってくれることを期待しています!

 最後に、校正を手伝ってくれた友人のNさんに感謝します。

グリボト!とBlockKingの紹介

自分が作ったDiscordBotの紹介です。

招待したい場合は、

  • 自身がサーバーの管理者権限を持っている場合
    • 招待urlにアクセスすると導入する画面が出てきます。権限についてはできる限り絞ってあります。
  • 持っていない場合
    • サーバーの管理者権限を持っている人に招待urlを渡してください。

グリボト!

f:id:soukouki:20190805010853p:plain
グリボト!の例

挨拶をすると返事をしてくれるbotです。省略形(こん)とか、挨拶からちょっと離れたものまで色々対応してます。

最近は誤反応も殆ど起こらなくなっています。つよいでしょ。

現在108サーバーに導入されていてます。

↓招待リンク。 discordapp.com

BlockKing

f:id:soukouki:20190805011359p:plain
BlockKingの例

Discord内でプレイできる半放置形ゲームです。

ブロックを支配し、アイテムを集め、クラフトし、どんどん強くなっていくゲームです。

放置時間が終わったらメンションで通知してくれるので、新しいことに目が行っちゃう人にもおすすめ!

目指せ王座!

現在101サーバーに導入されています。

↓公式サーバー(わからないことがあればこちらへ。プレイもできます。プレイヤー同士での会話とかもあります。) discord.gg

↓招待リンク discordapp.com

鉄道とバスで大内宿に行くときのメモ(2019-夏)

(ブログ書くのいつぶりだろう)

役立つかもしれないし役に立たないかもしれない情報集です。少なくとも自分は欲しかったです。 基本的に関東方面から向かうことを前提に書いています。

東武線特急リバティは下今市より北は特急券がいらない

railway.tobu.co.jp

貧乏旅で一瞬だけ豪華な時間を味わえます。

会津湯野上温泉から大内宿へはバスで行ける

ouchi-juku.com

猿遊号(さるゆうごう)が使えます。予約もできますが、必要ではありません。 運行期間は4月1日から11月30日までなので、冬には使えません。

地味に本数少ないので注意です。

猿遊号と会津鉄道の共通割引きっぷがある

会津鉄道&レトロバス猿游号で行く大内宿共通割引きっぷ │ 会津鉄道-会津鉄道で行く、会津の列車たびwww.aizutetsudo.jp

リバティで会津田島まで行き、そこで買うこともできますし、AIZUマウントエクスプレス車内でも買えると思います。(検札あったから車掌さんのはず!)

日光方面から会津若松へ抜けていく場合、

会津田島-湯野上温泉730円+猿遊号往復1000円+湯野上温泉-西若松840円=2570円が1900円になるのだから断然お得です!ちくしょう!

途中下車もできるので、塔のへつりにも寄れます。

会津田島-湯野上温泉の往復のきっぷもあります。案内見るとその区間内はどこにも行けるように見えるけど、使ったことがないのでわからないです・・・

あと、有効期限は2日間なので、途中の温泉で一泊する旅行でも使えます。

日光-喜多方(会津田島まで、芦ノ牧温泉までのきっぷもあり)

今回の自分の旅とはあまり合わなかったので、詳しいことはわからないです・・・

tabi.tobu.co.jp

4日も使えるので、色々使えそうです。

ファイルのダウンロード時に、ものすごい重くなる問題への解決策

ファイルをダウンロードするとき、すごく重い・・・数秒間フリーズしませんか・・?

※アップデートを忘れたため少し前のバージョンになるので、最新バージョンだとなにか変わっているかもしれません。


ダウンロードタブの右側のスクロールバーを上にスライドさせると、ものすごい昔のダウンロード履歴まで見られます。

なんとなく重そうなので、ダウンロードパネルの右上、箒のマークの「一覧からダウンロード済みのものをすべて削除」を押し、ついでに残ったものも削除してみるとあら不思議!

すごく軽く動くようになりました。今どれだけダウンロードが進んでいるかもわかるほど!

悩んでる方はぜひ。

以上、数カ月ぶりの更新のすごく短い文章でした。


操作をしたバージョン

Vivaldi 2.2.1388.37 (Stable channel) (32-bit)

現在の最新版

Vivaldi 2.3.1440.48 (Stable channel) (32-bit)

電卓をワンライナーで書きたい!

さて、4ヶ月ぶりの更新です。

まぁ、やることはタイトルのとおりです。

改行、セミコロンと、セミコロンに近いもの((a=2)&&([a,a]) #=>[2, 2])は縛っていきます。

まずは逆ポーランド式電卓から

f = ->(s){->(t){(t==s)? s : f[t]}[s.gsub(/(-?\d+) (-?\d+) ([-+\/*%])/){$1.to_i.send($3, $2.to_i)}]}

f["1 2 3 + *"] #=> "5"

アンダースタンディング コンピュテーションって本を読んでるとだんだんRubyラムダ式に慣れてくる・・

基本的に変数を使いたくなったらラムダ式に変換します。再帰でなければ・・・

仕組みは正規表現マッチとObject#sendを使って、正規表現にマッチしなくなるまで変換し続けていく形です。

このときは楽だった・・

そして、普通の電卓へ

f = ->(r){->(a){r[->(b){r[->(d){r[->(g){g.gsub(/(-?\d+)([*\/])(-?\d+)/){$1.to_i.send($2,$3.to_i)}},d].gsub(/(-?\d+)([-+])(-?\d+)(?![*\/])/){$1.to_i.send($2,$3.to_i)}},b].gsub(/\((-?\d+)\)/){$1}},a]}}[->(f,v){->(f){->(x){->(m){f[x[x]][m]}}[->(x){->(m){f[x[x]][m]}}]}[->(x){->(s){->(a){a==s ? a : x[a]}[f[s]]}}][v]}]

f["1*2/3"] #=> "0"
f["-1+2"] #=> "1"
f["1*2+3*4+5+6+7"] #=> "32"
f["-1*(2+3)"] #=> "-5"
f["1+2*(3+4)*5"] #=> "71"

お化けです。

このままじゃ読めないので、分解していきます。

r = ->(f,v){->(f){->(x){->(m){f[x[x]][m]}}[->(x){->(m){f[x[x]][m]}}]}[->(x){->(s){->(a){a==s ? a : x[a]}[f[s]]}}][v]}
f = ->(a){r[->(b){r[->(d){r[->(g){g.gsub(/(-?\d+)([*\/])(-?\d+)/){$1.to_i.send($2,$3.to_i)}},d].gsub(/(-?\d+)([-+])(-?\d+)(?![*\/])/){$1.to_i.send($2,$3.to_i)}},b].gsub(/\((-?\d+)\)/){$1}},a]}

まずは、rとfに分解します。

rの詳細とZコンビネータ

さらにrを分解します。

z = ->(f){->(x){->(m){f[x[x]][m]}}[->(x){->(m){f[x[x]][m]}}]}
r = ->(f,v){z[->(x){->(s){->(a){a==s ? a : x[a]}[f[s]]}}][v]}

(一つラムダ式とその呼出が増えていますが、動きは一緒です。)

元の形ではrの内部でrを呼び出して再帰をしていたのですが、この縛りだとrの名前がなくなるので、簡単に再帰はできません。

なので、Zコンビネータを使います。 obelisk.hatenablog.com(詳細は丸投げ!) (ぶっちゃけZコンビネータの中身は理解できてないし、これの使い方に試行錯誤する時間が多分一番長かった気がする)

fの詳細

ついでにfもわかりやすくしてみます。

# 参考用r(Zコンビネータ使用前)
r = ->(f,v){
    a=f[v]
    a==v ? a : r[f,a]
}
f = ->(a){
    r[
        ->(b){
            r[
                ->(d){
                    r[
                        ->(g){
                            g.gsub(/(-?\d+)([*\/])(-?\d+)/){$1.to_i.send($2,$3.to_i)}
                        },
                        d
                    ]
                        .gsub(/(-?\d+)([-+])(-?\d+)(?![*\/])/){$1.to_i.send($2,$3.to_i)}
                },
                b
            ]
                .gsub(/\((-?\d+)\)/){$1}
        },
        a
    ]
}

実はものすごいネストしています。

r(f,v)は値が変わらなくなるまでfを呼び続けます。逆ポーランド式電卓の方にもそんな感じの部分があります。

そしてそれをネストしながら呼び続けることによって、優先順位のある計算を行います。

括弧の処理は中の+-*/を計算し尽くしてできた(x)xに置き換えることによって行っています。

計算の様子

r内にp [f,v,a]を置いてみると、計算の様子がよくわかります。(Procの部分を置き換えています。)

(なんか括弧の優先順位が違うような気がするけど、とりあえず正常に動くので気にしない・・バグがあったらコメント等で教えてくれるとありがたいです。)

# f["1*2/3"]
[#<*/の処理>, "1*2/3", "2/3"]
[#<*/の処理>, "2/3", "0"]
[#<*/の処理>, "0", "0"]
[#<+-の処理>, "1*2/3", "0"]
[#<*/の処理>, "0", "0"]
[#<+-の処理>, "0", "0"]
[#<括弧の処理>, "1*2/3", "0"]
[#<*/の処理>, "0", "0"]
[#<+-の処理>, "0", "0"]
[#<括弧の処理>, "0", "0"]

# f["-1+2"]
[#<*/の処理>, "-1+2", "-1+2"]
[#<+-の処理>, "-1+2", "1"]
[#<*/の処理>, "1", "1"]
[#<+-の処理>, "1", "1"]
[#<括弧の処理>, "-1+2", "1"]
[#<*/の処理>, "1", "1"]
[#<+-の処理>, "1", "1"]
[#<括弧の処理>, "1", "1"]

# f["1*2+3*4+5+6+7"]
[#<*/の処理>, "1*2+3*4+5+6+7", "2+12+5+6+7"]
[#<*/の処理>, "2+12+5+6+7", "2+12+5+6+7"]
[#<+-の処理>, "1*2+3*4+5+6+7", "14+11+7"]
[#<*/の処理>, "14+11+7", "14+11+7"]
[#<+-の処理>, "14+11+7", "25+7"]
[#<*/の処理>, "25+7", "25+7"]
[#<+-の処理>, "25+7", "32"]
[#<*/の処理>, "32", "32"]
[#<+-の処理>, "32", "32"]
[#<括弧の処理>, "1*2+3*4+5+6+7", "32"]
[#<*/の処理>, "32", "32"]
[#<+-の処理>, "32", "32"]
[#<括弧の処理>, "32", "32"]

# f["-1*(2+3)"]
[#<*/の処理>, "-1*(2+3)", "-1*(2+3)"]
[#<+-の処理>, "-1*(2+3)", "-1*(5)"]
[#<*/の処理>, "-1*(5)", "-1*(5)"]
[#<+-の処理>, "-1*(5)", "-1*(5)"]
[#<括弧の処理>, "-1*(2+3)", "-1*5"]
[#<*/の処理>, "-1*5", "-5"]
[#<*/の処理>, "-5", "-5"]
[#<+-の処理>, "-1*5", "-5"]
[#<*/の処理>, "-5", "-5"]
[#<+-の処理>, "-5", "-5"]
[#<括弧の処理>, "-1*5", "-5"]
[#<*/の処理>, "-5", "-5"]
[#<+-の処理>, "-5", "-5"]
[#<括弧の処理>, "-5", "-5"]

# f["1+2*(3+4)*5"]
[#<*/の処理>, "1+2*(3+4)*5", "1+2*(3+4)*5"]
[#<+-の処理>, "1+2*(3+4)*5", "1+2*(7)*5"]
[#<*/の処理>, "1+2*(7)*5", "1+2*(7)*5"]
[#<+-の処理>, "1+2*(7)*5", "1+2*(7)*5"]
[#<括弧の処理>, "1+2*(3+4)*5", "1+2*7*5"]
[#<*/の処理>, "1+2*7*5", "1+14*5"]
[#<*/の処理>, "1+14*5", "1+70"]
[#<*/の処理>, "1+70", "1+70"]
[#<+-の処理>, "1+2*7*5", "71"]
[#<*/の処理>, "71", "71"]
[#<+-の処理>, "71", "71"]
[#<括弧の処理>, "1+2*7*5", "71"]
[#<*/の処理>, "71", "71"]
[#<+-の処理>, "71", "71"]
[#<括弧の処理>, "71", "71"]

おまけ

製作途中

r = ->(f,v){a=f[v];a==v ? a : r[f,a]}
f1 = ->(s){r[->(t){t.gsub(/(-?\d+)([*\/])(-?\d+)/){$1.to_i.send($2,$3.to_i)}},s]}
f2 = ->(s){r[->(t){f1[t].gsub(/(-?\d+)([-+])(-?\d+)(?![*\/])/){$1.to_i.send($2,$3.to_i)}},s]}
f = ->(s){r[->(t){f2[t].gsub(/\((-?\d+)\)/){$1}},s]}

ハマったところ

  • rの再帰の呼び出しr[f,a]f[a]にしていて、なんかおかしいなと思いつつも大体の式はうまく動いちゃうので微妙なバグでした。
  • f1は楽だけど、f2のf1を呼ぶ部分で迷った・・3重ループになるはずだから、多分ここで合ってるはず・・実際式を入れたら動くのだし()
  • Zコンビネータの使い方・・あれって2つの引数を持つ再帰はできないのかな・・?最初fも再帰の引数に入れてて詰まってました。

以上

秋の夜長にプログラミング!そして次の日の朝起きられなくなる・・・