Language detail: xtal

Coverage: 17.14%
number of '+' ratings
contribution for coverage

Unsolved challenges

codes

Feed

Used modules

next >>

正整数のゲーデル数化? (Nested Flatten)
nobreak 便利。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
doukaku100: fun(n){
  primes: fiber(){
    yield 2;
    for(n: 3;; n += 2)
      for(d, m : 3, math::sqrt(n); d < m; d += 2)
        if(n % d == 0) break; nobreak yield n;
  }
  r: 1;
  n.to_s.split("").zip(primes){|d, p| r *= math::pow(p, d.to_i); }
  return r;
}
自然数の分割 (Nested Flatten)
fiber を使って書き換えてみる。
1
2
3
4
5
6
(|n, m|{ c: callee; m--;
  return fiber{
    if(m < 1) yield [n];
    else (n + 1).times{|x| c(x, m){ yield [n - x] ~ it; } }
  }
})(5, 3).join("\n").p;
1
2
3
4
5
6
(|n, m|{
  if(m <= 1) return [[n]];
  r: [];
  (n + 1).times{|x| callee(x, m - 1){ r.push_back([n - x] ~ it); } }
  return r;
})(5, 3){ it.p; }
2進数の記述 (Nested Flatten)
0b か 0B で始めると2進整数リテラル。
1
2
3
4
5
0b1010.p;
// => 10

0B0110_1001.p;
// => 105 (アンダーバーは無視される)
九九の表示 (Nested Flatten)
%f はフォーマット文字列を定義するリテラルです。
formatter は引数を3つ取る関数になります。
%(hogehoge)d とすることで名前付きにすることも できます。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
mul: fun(n, m) fiber {
    n.times.map(|i| i+1).each{|i|
        m.times.map(|j| j+1).each{|j|
            yield i, j, i * j;
        }
    }
}

formatter: %f[%d * %d = %2d];
mul(9, 9) {|i,j,s|
    formatter(i, j, s).p;
}
重複する要素を取り除く (Nested Flatten)
配列の要素を [その要素、*残りの要素] に map して
「残りの要素」に「その要素」が含まれていなければ yield します。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
Iterator::skip: method(n) {
    n.times { this { break; } }
}
Iterator::window: method(start, length) {
    this.skip(start);
    return this.take(length);
}

only_uniq: fun(arr) fiber {
    len: arr.length;
    iters: range(0, len).map(|i| arr.cycle.window(i, len).to_a);
    iters {|it,rest|
        if (rest.select(|e| e == it).to_a.empty) {
            yield it;
        }
    }
}

only_uniq([3, 1, 4, 1, 5, 9, 2, 6, 5]).to_a.p;
仲間はずれの判定 (Nested Flatten)
辞書などを使った回答はもうずいぶん出ているので違ったアプローチで書いたら、
ずいぶん長くなってしまいました。。

実装はひとつづつ要素を見ながら状態遷移していくものです。
each_classify で状態遷移を観察することができます。

無限列にも適用可能です。
もちろん本当に無限にある場合は「その他」以外では答えを出せないのですが、
無限列の最初の n 個の仲間はずれを判定できます。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
Classify: class {
    - _iter;
    - _state: "homo";
    - _majority: null;
    - _minority: null;

    initialize: method(_iter) {}

    + classify: method(n: null) {
        return each_classify(n).to_a.back;
    }

    + each_classify: method(n: null) fiber {
        iter: n ? _iter.take(n) : _iter;
        iter.with_index {|i,it|
            this.(_state)(i, it);
            if (_state == "hetero") {
                break;
            }
        }
    }

    - homo: method(i, it) {
        if (_majority && _majority != it) {
            _minority = it;
            if (i < 2) {
                _state = "quasi_homo0";
                yield ["?", _majority, _minority];
            } else {
                _state = "quasi_homo";
                yield ["quasi_homo", _majority, _minority];
            }
        } else {
            _majority = it;
            yield ["homo", it];
        }
    }

    - quasi_homo0: method(i, it) {
        if (it == _majority || it == _minority) {
            if (it == _minority) {
                _majority, _minority = _minority, _majority;
            }
            yield ["quasi_homo", _majority, _minority];
            _state = "quasi_homo";
        } else {
            hetero(i, it);
        }
    }

    - quasi_homo: method(i, it) {
        if (it == _majority) {
            yield ["quasi_homo", _majority, _minority];
        } else {
            hetero(i, it);
        }
    }

    - hetero: method(i, it) {
        _state = "hetero";
        yield ["hetero", null];
    }
}

Classify([1,1,1,1,1].each).classify.p;    //=> [homo,1]
Classify([1,2,1,1,1].each).classify.p;    //=> [quasi_homo,1,2]
Classify([2,1,1,1,1].each).classify.p;    //=> [quasi_homo,1,2]
Classify([2,2,1,1,1].each).classify.p;    //=> [hetero,null]
Classify([2,4,1,1,1].each).classify.p;    //=> [hetero,null]
Classify([1,2,3,4,5].each).classify.p;    //=> [hetero,null]

Classify([2,1,1,1,3].each).each_classify {
  it.p;
}
//=> [homo,2]
//=> [?,2,1]
//=> [quasi_homo,1,2]
//=> [quasi_homo,1,2]
//=> [hetero,null]

Classify([1,1,3].cycle).classify(5).p;    //=> [quasi_homo,1,3]
Classify([1,1,3].cycle).classify(6).p;    //=> [hetero,null]
Classify([1,2,3].cycle).classify.p;       //=> [hetero,null]
与えられた文字列でピラミッド (Nested Flatten)
末尾の空白の数がサンプル出力と違いますが、題意になかったのでスルーしました。

chainは引数のイテレータを順番に実行するイテレータを返す関数です。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
pyramid: fun(str) {
    space: [" "].cycle;
    arr: str.split("").to_a;
    n: arr.length;
    n.times.map(|i| i + 1){
        chain(space.take(n - it ),
              arr.slice(n - it, n).zip(space).map(|a,b| a~b)).join("").p;
    }
}

pyramid("hoge");
pyramid("abracadabra");
隣り合う二項の差 (Nested Flatten)
最初 diff は Array を引数に取り、その Array の Iterator と一個先に進めたIterator を zip して map していたんですが、無限列を diff したくなったので Array ではなく Iterator を引数に取るようにしました。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Iterator::each_pair: method fiber {
    prev: null;
    this { prev = it; break; }
    this {
        yield prev, it;
        prev = it;
    }
}

diff: fun(iter) {
    return iter.each_pair.map(|a,b| b - a);
}

diff([3, 1, 4, 1, 5, 9, 2, 6, 5].each).to_a.p;


// 無限列のテスト
random_generator: fiber {
    while (true) {
        yield math::ceil(math::random_range(1, 10));
    }
}
diff(random_generator).take(20).to_a.p;
アルファベットの繰り上がり (Nested Flatten)
前の桁で生成した値の頭にA..Zを付加するという戦略です。

無限桁に対応。A-Zの26進数以外もいけます。

ただ、メモリの消費量が激しいです。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
ExcelNumber: class {
    + _chars: "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

    + generate: method fiber {
        before: [""];
        while (true) {
            before = generate_and_yield(before);
        }
    }

    - generate_and_yield: method(before) {
        r: [];
        _chars.split("") {|c|
            before {|b|
                n: c ~ b;
                r.push_back(n);
                yield n;
            }
        }
        return r;
    }
}

excelnum: ExcelNumber();
excelnum.generate.take(100).to_a.p;

excelnum.chars = "abc";
excelnum.generate.take(100).to_a.p;
長方形の交差判定 (Nested Flatten)
javascriptの投稿(http://ja.doukaku.org/comment/1146/)がすっきりしててかっこよかったのでそのままもらいました。

svn rev154 (0.9.7.1)です。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
Rectangle: class {
  + _top; + _right; + _bottom; + _left;

  initialize: method(top: 0, right: 0, bottom: 0, left: 0) {
    _top = top;
    _right = right;
    _bottom = bottom;
    _left = left;
  }

  is_overlapped: method(other) {
    return (math::min(_right, other.right) - math::max(_top, other.left) > 0 &&
            math::min(_bottom, other.bottom) - math::max(_left, other.top) > 0);
  }

  to_s: method() {
    return [_top, _right, _bottom, _left].to_s;
  }
}

r1: Rectangle(top: 10, right: 20, bottom: 20, left: 10);
[ // overlapped
  Rectangle(top: 10, right: 20, bottom: 20, left: 10), // 一致
  Rectangle(top: 11, right: 19, bottom: 19, left: 11), // 内
  Rectangle(top: 9, right: 21, bottom: 21, left: 9),   // 外
  Rectangle(top: 5, right: 15, bottom: 15, left: 5),   // 左上
  Rectangle(top: 5, right: 25, bottom: 15, left: 15),  // 右上
  Rectangle(top: 15, right: 25, bottom: 25, left: 15), // 右下
  Rectangle(top: 15, right: 15, bottom: 25, left: 5),  // 左下
  // ! overlapped
  Rectangle(top: 0, right: 10, bottom: 10, left: 0),   // 左上
  Rectangle(top: 0, right: 20, bottom: 10, left: 10),  // 上
  Rectangle(top: 0, right: 30, bottom: 10, left: 20),  // 右上
  Rectangle(top: 10, right: 30, bottom: 20, left: 20), // 右
  Rectangle(top: 20, right: 30, bottom: 30, left: 20), // 右下
  Rectangle(top: 20, right: 20, bottom: 30, left: 10), // 下
  Rectangle(top: 20, right: 10, bottom: 30, left: 0), // 左下
  Rectangle(top: 10, right: 10, bottom: 20, left: 0), // 左
].each {|r| r1.is_overlapped(r).p; }
実行時間の測定 (Nested Flatten)
0.9.7 から追加された(と思われる) clock() 関数を使ってみました。

* profile 関数と func 関数が同じ引数を受け取れる。
* func 関数の戻り値を profile 関数の戻り値としている。

svn rev154 (0.9.7.1)です。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
profile: fun(...) {
  fmt: %f!profile: %d sec!;

  start: clock();
  result: func(...);
  fmt(clock() - start).p;

  return result;
}

func: fun(...) {
  for (i: 0; i < 1000000; i++) {
    // 重たい処理
  }
}

profile("test");
メソッド名一覧の表示 (Nested Flatten)
super クラスも検索するようにしてみました。

「bar: || "bar".p」というのは引数なしの lambda 関数を bar に束縛しているような感じです。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
String::start_with: method(sub) {
    return this.split("").take(sub.length).to_a.join("") == sub;
}

TargetTest: class {
    test_foo: || "test_foo".p;
    test_bar: || "test_bar".p;
    foo: || "foo".p;
    bar: || "bar".p;
}
TargetTest2: class(TargetTest) {
    test_foo: || "test_foo_sub".p;
    test_baz: || "test_baz".p;
}

TestRunner: class {
    suite: method(target, inherited_too: true) fiber {
        klasses: [target];
        if (inherited_too) {
            klasses ~= target.each_ancestor.to_a;
        }

        override: Map();
        klasses {|klass|
            klass.each_member {|name, x, meth|
                if (! override[name] && name.start_with("test_")) {
                    yield meth;
                }
               override[name] = true;
            }
        }
    }

    run: method(target) {
        suite(target) {
            it();
        }
    }
}

runner: TestRunner();
"---".p;
runner.run(TargetTest2);
"---".p;
runner.run(TargetTest);
Hello, world!その2 (Nested Flatten)
xtal 0.9.7 から識別子に日本語が使えるようになったのでそれで。
なでしこのが参考になりました。
1
2
3
4
5
6
7
へ: "He";
ろ: "l";
お: "o";
わ: "w";
る: "r";
ど: "d!";
(へ ~ ろ ~ ろ ~ お ~ ", " ~ わ ~ お ~ る ~ ろ ~ ど).p;
全ての組み合わせ (Nested Flatten)
fiber でジェネレータにしました。

「(...)」 というのは可変長引数をあらわすオブジェクトです。でも、コレ使うのなんかクラッシュする。。。

あと、配列の分配がきれいにかけないです。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
CrossProduct: class {
    - _list: [];

    initialize: method(...) {
        (...).each_ordered_arg {
            // ここのコメントを削除するとなぜかクラッシュする^^
            if (it is String) {
                _list.push_back(it.split("").to_a);
            } else {
                _list.push_back(it);
            }
        }
    }

    all: method fiber {
        (fun (acc, rest) {
            if (rest.empty()) {
                yield acc;
            } else {
                x, xs: rest[0], rest.slice(1, rest.length);
                x {
                    callee(acc ~ [it], xs);
                }
            }
        })([], _list);
    }
}

c: CrossProduct([0, 1], "ab", ["Foo", "Bar"]);
c.all {
    it.p;
}
与えた条件を満たす候補 (Nested Flatten)
not を何回も書けるようにしました。
and とか or みたいに1行メソッドがシンプルに書けるのがいいですね。

あと、xtal は callee があるので無名関数で再帰できるのもいいところ。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// xtal 0.9.7.1 の inject はバグっているので自作。
Iterator::reduce: method(init, fn){
    result: init;
    this {
        result = fn(result, it);
    }
    return result;
}

Solver: class {

    - _expr;

    initialize: method(_expr) {}

    and: |a,b| a && b;
    or:  |a,b| a || b;

    apply_not: method(values) {
        r: values.clone();
        i: 0;
        _expr.each {
            if (it == "not") {
                r[i] = ! r[i];
            } else {
                i++;
            }
        }
        return r[0], r.slice(1, r.length);
    }

    eval: method(values) {
        first, rest: apply_not(values);
        expr: _expr.select(|e| e != "not");
        return expr.zip(rest.each).reduce(first, fun(r, pair) {
            // this.("and") で and メソッドが取得できる
            return this.(pair[0])(r, pair[1]);
        });
    }
}

prodloop: method(n) fiber {
    (fun(n, xs) {
        if (n == 0) {
            yield xs;
        } else {
            [true, false] {
                callee(n - 1, xs ~ [it]);
            }
        }
    })(n, []);
}

solver: Solver(["and", "or", "not", "not", "not", "and"]);
prodloop(4).select(|v| solver.eval(v) ) {
    it.p;
}
倍数になる13進数 (Nested Flatten)
無駄に無限リストにしてみました。
遅くて3個目が見つからない。。。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// xtal 0.9.7.1 の inject はバグっているので自作。
Iterator::reduce: method(init, fn){
    result: init;
    this {
        result = fn(result, it);
    }
    return result;
}


Int::to_i13: method {
    return this.to_s.split("").reduce(0, |r,e| r * 13 + e.to_i);
}

resolver: fiber {
    for (i: 10; true; i++) {
        if (i.to_i13 % i == 0) {
            yield i;
        }
    }
}

resolver.take(1).to_a[0].p;
RFC 4180対応版 CSVレコードの分解 (Nested Flatten)
汚いなぁ。。。
fiber 便利。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
Iterator::with_peek: method fiber {
    prev, curr: null, null;
    noloop: true;
    this {|next|
        if (curr) {
            yield prev, curr, next;
        }
        prev, curr = curr, next;
        noloop = false;
    }
    if (!noloop) {
        yield prev, curr, null;
    }
}

CSVParser: class {
    - _iter;
    - _finish;

    initialize: method(string) {
        _iter = string.split("").each.with_peek;
        _finish = false;
    }

    flush: method(field) {
        r: field.join("");
        field.clear();
        return r;
    }

    line_parser: method fiber {
        in_quote: false;
        field: [];
        _iter {|prev,it,next|
            if (in_quote) {
                if (it == "\"") {
                    if (prev == "\"") {
                        // ignore
                    } else if (next == "\"") {
                        field.push_back(it);
                    } else {
                        in_quote = false;
                    }
                } else {
                    field.push_back(it);
                }
            } else {
                if (field.empty() && it == "\"") {
                    in_quote = true;
                } else if (it == "\n") {
                    yield flush(field);
                    break;
                } else if (it == ",") {
                    yield flush(field);
                } else {
                    field.push_back(it);
                }
            }
        } nobreak {
            _finish = true;
        }
        if (!field.empty()) {
            yield flush(field);
        }
    }

    parse: method fiber {
        while (!_finish) {
            yield line_parser();
        }
    }
}


parser: CSVParser(
    [%!"aaa","b\nbb","ccc",zzz,"y""Y""y",xxx!,
     %!a,b,c,d!,
     %!a,b,c,!,
     %!a!,
     %!!,
     ].join("\n"));

format: %f[%(line)d:%(col)d: %(cell)s];
parser.parse.with_index {|lineno,line|
    line.with_index {|colno,it|
        format(line: lineno, col: colno, cell: it).p;
    }
}
リストを逆順に表示 (Nested Flatten)

	
1
2
3
4
5
6
7
8
9
Iterator::reverse: method fiber {
    arr: this.to_a;
    while (!arr.empty()) {
        yield arr[arr.size - 1];
        arr.pop_back();
    }
}

[1, 2, 3, 4, 5].each.reverse.to_a.p;
/*コメント*/を取り除く (Nested Flatten)
ひたすら一文字づつ見てパースしました。正規表現ほしぃ。。。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Iterator::with_peek: method fiber {
    prev, curr: null, null;
    noloop: true;
    this {|next|
        if (curr) {
            yield prev, curr, next;
        }
        prev, curr = curr, next;
        noloop = false;
    }
    if (!noloop) {
        yield prev, curr, null;
    }
}

remover: fun(str) fiber {
    in_comment: false;
    str.split("").each.with_peek {|prev,it,next|
        if (in_comment) {
            if (prev == "*" && it == "/") {
                in_comment = false;
            }
        } else {
            if (it == "/" && next == "*") {
                in_comment = true;
            } else {
                yield it;
            }
        }
    }
}

remove_comment: fun(str) {
    return remover(str).to_a.join("");
}

assert remove_comment("AAA") == "AAA";
assert remove_comment("AAA/*BBB*/") == "AAA";
assert remove_comment("AAA/*BBB") == "AAA";
assert remove_comment("AAA/*BBB*/CCC") == "AAACCC";
assert remove_comment("AAA/*BBB/*CCC*/DDD*/EEE") == "AAADDD*/EEE";
assert remove_comment("AAA/a//*BB*B**/CCC") == "AAA/a/CCC";
next >>

Index

Feed

Other

Link

Pathtraq

loading...