Array.prototype.forEach
は途中でbreakしたくなっても抜けられない。
Array.prototype.some
を使うという方法もあるみたいですが、それは色々とあれなので…
とりあえず、Scala風にBreaksクラスを作ってみた。(scala.util.control.Breaks)
breaks.js
/**
* scala.util.control.Breaksのようなもの。
* @constructor
*/
function Breaks() {
if (!(this instanceof Breaks)) return new Breaks();
var
value,
breaking = {};
/**
* ループを抜ける
* @name breaks
* @fieldOf Breaks
*/
Object.defineProperty(this, 'breaks', {
get: function () {
throw breaking;
},
set: function (val) {
value = val;
throw breaking;
},
});
/**
* 抜けられるしキャッチできるブロック
* @param {Function} procedure 実行するブロック
* @return {Object} catchBreakを持ったオブジェクト
*/
this.tryBreakable = function (procedure) {
var
breaks = this;
return {
catchBreak: function (onBreak) {
value = undefined;
try {
procedure(breaks);
} catch(e) {
if (e !== breaking) throw e;
onBreak(value);
}
},
};
};
}
/**
* 抜けられるブロック
* @param {Function} procedure 実行するブロック
*/
Breaks.prototype.breakable = function (procedure) {
this.tryBreakable(procedure).catchBreak(function () { /* noop */ });
};
/**
* インスタンスをわざわざ作るのが面倒な人向け
* @param {Function} procedure 実行するブロック
*/
Breaks.breakable = function (procedure) {
(new Breaks).breakable(procedure);
}
コードを見れば一目瞭然だけど、scala.util.control.Breaks同様に特定の例外を投げてそれをキャッチすることでbreakを実現してる。
使い方はこうなる。
breaks-usage.js
Breaks.breakable(function (b) {
[1, 2, 3, 4, 5].forEach(function (x) {
if (x === 3) b.breaks;
console.log(x);
});
});
結構良い感じだと思う。
実は、最初はCoffeeScriptで書いてたんだけど、よく考えたらCoffeeScriptには優秀なfor文があるから必要ないなー、と思ったりしてました。
一応その記録も貼っておきます。
breaks.coffee
getter = (obj, name, func) ->
Object.defineProperty obj, name,
get: func
class Breaks
constructor: ->
breaking = {}
getter @, 'breaks', -> throw breaking
@breakable = (procedure) ->
try
procedure @
catch e
throw e if e isnt breaking
@breakable: (procedure) ->
(new Breaks).breakable procedure
Breaks.breakable (b) ->
[1..10].forEach (x) ->
b.breaks if x is 5
console.log x
これはこれでいいと思ったんだけどな―