Nim
言語処理100本ノック

Nimで言語処理100本ノック 02. 「パトカー」+「タクシー」=「パタトクカシーー」

問題

言語処理100本ノック 2015#sec02

「パトカー」+「タクシー」の文字を先頭から交互に連結して文字列「パタトクカシーー」を得よ.

考え方

2重ループではなくて、両方から1文字ずつ空の変数に注ぐカフェオレのようなイメージ。

P = 'パトカー'
T = 'タクシー'

PT = ''

for(i=0; i < max(T.length,P.length); i++) {
  if (i < T.length) {
    PT += T[i]
  }
  if (i < P.length) {
    PT += P[i]
  }
}

echo PT

順当に考えるとこんなもの。
でもなんだか交互という感じがしないし、maxで無理やり長い方に合わせているのも気持ち悪い?
最後まで読み込み終わった感が欲しい。
そしてこう、一文字とりだしたらピタっと止まる感覚が欲しくなった。
(普段は十中十割上の方法でやるでしょう)

任意のタイミングで次の一文字を取り出すとなると、前回の状態を記憶しておく必要があるので、ややこしい。

staticならこんな感じ?

function 一文字取り出す($str) {
  static $i = 0
  return $i < strlen($str) ? $str[$i++] : '';
}

一文字取り出す('パトカー');
# タクシーに使えない…

PHPの静的変数 (static変数) の挙動まとめ - Qiita

jsなら

const fnc = ((str) => {
  let i = 0
  return () => {
    return i < str.length ? str[i++] : ''
  }
})

const T = fnc('タクシー')
T();

な感じ?

Nimならどうすっか…
というところで目に入った

至高の言語、Nimを始めるエンジニアへ - Qiita #FizzBuzzのイミュータブルな解決法は?

のイテレータ版が、好きなときに一文字とってこれる感が出て良さそうだと思いました。
普段イテレータなんて絶対作らないのでたまには…というのも込めて。

解答

nim2.nim
import unicode

const str1 = "パトカー"
const str2 = "タクシー"

# https://nim-by-example.github.io/for_iterators/
proc makeGetRuneIterator(str: string): iterator(): Rune =
  return iterator(): Rune = 
    for c in toRunes(str) :
      yield c

var newStr: seq[Rune] = @[]

let str1Ite = makeGetRuneIterator(str1)
let str2Ite = makeGetRuneIterator(str2)

while not finished(str1Ite) or not finished(str2Ite):
  if not finished(str1Ite):
    newStr.add(str1Ite())
  if not finished(str2Ite):
    newStr.add(str2Ite())

echo newStr

別々の文字列を持った別々のイテレータが欲しいのでClosure Iterators?パターンになりました。
関数?を返す関数?を作ると上級者っぽい気分とコードを複雑にしている気分を味わえます。

イテレータの良いと思ったことは走査終了がfinishedで調べられることですね。わかりやすいです。
ただforなどに渡さないのはイテレータとしてすっきりしないかもです。

既存解答例について

Pythonではzipを使う方法がよく挙げられているようですが、
あれはなんとなくしっくりこなかったです。

nimにもzipはあるので、似たように書けそうです。
Module sequtils#zip

ただ、短いほうに合わせるzipのようです。Pythonと同じですね。

前回

Nimで言語処理100本ノック 01. 「パタトクカシーー」 - Qiita