ほげほげー

C#メインにプログラミング周りから日常のあれこれとかを不定期に書いていきます。

JavaScript における yield の話とかアロー関数の話とか

JavaScript で業務用のブックマークレットでも書くかと思って書いてたら何か自分が知ってる JavaScript からだいぶ変わってたのでメモがてら。

最近の JavaScript について

もはや JavaScript を勉強したのは何年前だって状態なので、間の機能を補完しようかと思ったのですが、何が増えているのかさっぱりわからないのでとりあえず yieldラムダ式 アロー関数 の話でも。async/await もいいんですが、まぁこれは気が向いたら。

yield について

yield は、委譲するとか譲るとかの意味を持った英単語で、実際 yield の使用時には値を実処理側に委譲するような動きをします。

記述方法は以下の形式です。

function* sample() {
  yield "one";
  yield "two";
  yield "three";
}

function の後にアスタリスクを付けることで yield キーワードが使えるようになります。

この関数の戻り値は Generator 型となり、Generator.prototype.next() 関数や for of*1 を使って要素を一つずつ処理することができます。

let values = sample();
console.log(values.next()); // {value: "one", done: false}
console.log(values.next()); // {value: "two", done: false}
console.log(values.next()); // {value: "three", done: false}
console.log(values.next()); // {value: undefined, done: true}

ループで回して値を取得する場合は

let values = sample();
while ((let next = values.next()).done === false) {
  console.log(next.value);
}

または

let values = sample();
for (const value of values) {
  console.log(value);
}

で利用できます。基本は後者ですかね。

これができると何が嬉しいかというと、列挙処理において遅延評価がしやすくなる点と、同じことをしようとした場合に Iterator パターンを一から実装しないといけなかったものが簡潔に書けるようになった点にあります。

C# でも yield return でほぼ同様のことができる実現でき、それについてはこちらに諸々まとめてあります。

Generator 型について

さて、C# を慣れ親しんだ身としては yield で返された値は LINQ っぽく扱いたくなるもので、これは Generatorprototype を拡張してやればできるはずです。

とりあえず拡張して U Select<T, U>(this IEnumerable<T, U> source, Func<T, U> selector) 的なものを足してみましょう。

Generator.prototype.select = function*(seletor){for (item of this) { yield selector(item); }};

すると、Generator が見つからず、エラーになってしまいます。

Generator は直接インスタンスを生成することが許可されていないため、触れる場所にはありません。

では yield で返ってきた Generatorprototype を使ってみましょう。

const generator = (function*(){})();
generator.__proto__.select = function*(seletor){for (item of this) { yield selector(item); }};

できたっぽいので使ってみましょう。

const sample = (function*() {
  yield 1;
  yield 2;
  yield 3;
})();
for (const item of sample.select(i => i * 10)) {
  console.log(item);
}

実行すると以下のエラーが発生します。

VM2202:7 Uncaught TypeError: s.select is not a function or its return value is not iterable

何故でしょうか?

C# で yield return に触れていると何となく原因はこれなのかな?
というものが頭に浮かびます。

yield は結局のところシンタックスシュガーで、Generator は実行エンジン側で動的に生成された型になっているのです。

つまり Generator を生成する関数毎に異なる型が返却されていることになります。

そこでデバッグコンソールからオブジェクトのプロトタイプチェーンを眺めてみると

f:id:tyhe:20210109020603p:plain

どうやら Generator の上にさらに Generator がいました。

こいつがどうやら大元っぽいのでこいつを拡張してみましょう。

const generator = (function*(){})();
generator.__proto__.__proto__.select = function*(seletor){for (item of this) { yield selector(item); }};

で、これを使ってみましょう。

const sample = (function*() {
  yield 1;
  yield 2;
  yield 3;
})();
for (const item of sample.select(i => i * 10)) {
  console.log(item);
}

動きました。

これで Generator の拡張ができるようになりました。

any や all も欲しいのでこちらも実装してみましょう。

今回は Generator を返す必要がないので折角なのでアロー関数で書いてみましょう。

generator.__proto__.__proto__.any = (predicator = (_ => true)) => {
  for (const item of this) {
    if (predicator(item)) {
      return true;
    }
    return false;
  }
}

さて実行してみましょう。

const sample = (function*() {
  yield 1;
  yield 2;
  yield 3;
})();
if (sample.any()) {
  console.log("success"):
}

実行すると以下のエラーが発生します。

VM1280:2 Uncaught TypeError: this is not iterable
    at Generator.generator.__proto__.__proto__.any

this が iterable じゃないと言われています。なるほど?

試しに function で再実装してみましょう。

generator.__proto__.__proto__.any = function(predicator = (_ => true)) {
  for (const item of this) {
    if (predicator(item)) {
      return true;
    }
    return false;
  }
}

そして実行。

if (sample.any()) {
  console.log("success"):
}

見事動きました。何故でしょう?

MDN を見ると以下のように記述されています。

this, arguments, super, new.target を束縛しません。

つまり this は記述されている環境に依存した値のまま変わらないということになります。

アロー関数は関数の引数に使用するなどに止めるのが良さそうですね。

*1:iterable protocol を満たす場合に for of を使うことができる

日本版 HUAWEI Mate 30 Pro 5G を先行販売で買ったので軽くレビュー

ご存知の方も多いと思いますが、HUAWEIアメリカからの制裁によって Google Mobile Services (以下 GMS) を載せることができなくなっています。

日本で出る端末では HUAWEI nova 5T までは載っていますが、これは割と特殊事例で、基本的には今後販売する端末については GMS は載せられません。

そんな中 GMS 非搭載端末として初めて日本で販売される Mate 30 Pro を先行販売で購入してきたので使い勝手やカメラの性能などを軽くレビューしていきたいと思います。

f:id:tyhe:20200329125215j:plain
こちらは Mate 30 Pro で撮ったピザ

続きを読む

楽天モバイル(MNO)で eSIM のアクティベートに失敗した話

前置き

https://network.mobile.rakuten.co.jp/cms/faq/detail/00000890

楽天回線でeSIMに対応している製品はRakuten Miniのみとなります。 その他のeSIM対応製品の動作確認は保証しておりませんのでご了承ください。

と、あるように Rakuten Mini 以外での動作は保証されていませんのでご注意ください。

続きを読む

マウスとキーボードをワイヤレス化したい人に向けて

色々思うところがあって、コードをなるべく減らそうと思い立ちまして、キーボードとマウスをワイヤレスにしました。

ワイヤレスにするにあたって考えたこと、何故それを選んだか等をまとめておこうと思います。

打鍵感重視の人には恐らく参考になるとは思いますが、打鍵感の良し悪しは人によって様々ですので、
実際に購入される際には店頭で触ってみることをおすすめします。

続きを読む

Windows で US 配列と JIS 配列を共存させたい

ゲーミングノートPC買ってしまいました

昨日デスクトップが壊れかけたのでコードを減らしたいという思いもあり、 かつ持ち運びもしたいということで、勢いで MAX-Q Design なノートパソコン買ってしまいました。

手元にあるキーボードはUS配列ですが、ノートPC本体のキーボードはJIS配列ですが、Windows ではデフォルトで JIS 配列と US 配列の混在がうまく扱えません。

本項ではこちらの使い分けの方法を記載します。

続きを読む