カーネルおじさんによる美味しくできるニワトリ料理パターン

www.adventar.org

╰( ´◔ ω ◔ `)╯ 毎回記事本体じゃなくて、ナンセンス釣りタイトルを考えるのに時間を費やしてる気がしなくもない

今朝は Swiff の型について考えたような気がする。
自分の中では Swiff も Uva も文法的なところが違うだけのようになってきたので、どんどこどーん!

Swiff は Swift や Rust、Kotlin をインスペクトしているので、let は変数宣言で、val は定数宣言になってる。

すごく簡単に書くとこんな感じ:

("let" | "val") <識別子> [":" <型ヒンティング>] "=" <値> ";"

val だった場合は、<値> 必須だとか色々あるけど、こんな風に変数または定数を定義する。
例を示すとこんな感じ:

let hoge = 42;
let piyo : string = "Cunky bacon!!";

val TeddyBearFee = 6000;

上記の例で val で宣言された識別子、ええっと、定数が UpperCamelCase で表記されていることに気がついた人もいるかもしれない。
そして、定数が UpperCamelCase でないとコンパイラがブーブー言うだろうと思った人は安心していい。
そんなことはない。

Wikipedia の Swift の記事において var*1 で宣言された識別子は定数と呼ばれていた。
でも、そうじゃなくて、変更できない変数あるいは束縛と呼ぶべきだったのだ。
典型的な関数型プログラミングの考え方では、私たちが変数と呼ばれているものは実は値に名前が書かれた札が紐で結ばれているだけで、 それは分かちがたく結びついているので変更できない。
このことを不変更性を持つと呼ぼう。
札から下がった紐が果てしなくこんがらがって絶対に解くことはできないのだ。

とはいえ、実用的なプログラミング言語にそのようなパズルみたいな難解な代物を持ち込むべきではないと今でも思っている人たちも大勢いるわけで、 彼らにも配慮してやらなければならない。

私としては不変更性はすごく重要だと思っているので Uva では普通に宣言した変数は不変更性を持つようになっている。
どうしても変更可能な変数を宣言したい場合は variable アノテーションをつければいい。

Swiff はカジュアルな言語を志向しているので、型ヒンティングは :(コロン)で識別子の後ろに付けることになる。
もちろん、そんな面倒なことをしなくても、型推論によってある程度変数の型はコンパイル時に決定される。
Uva でも型推論で同じように変数の型は決定されるけど、そうしたい旨をコンパイラに伝えないといけない。
それが auto キーワードだ。
Uva は Swiff の後置に対して前置タイプなので、型ヒンティングを必ず付ける必要が生じる*2
でも、Uva には静的型付き言語の顔をしてほしかったため、C++ から auto を取ってきた。

(。ŏ﹏ŏ) こうしてみると、Uva と Swiff って性格が違うんだね

╰( ´◔ ω ◔ `)╯ そうだな

更に、関数定義でも fun ではなく、def を採用した。

def is_nil(x : T?) : bool {
    match x {
        is T    => true,
        is Nil  => false
    }
}

あと、match 式 (・∀・)イイ!!
という感じだったので、switch は無し。

return もあるけど、基本的には最後の値が返されるという感じ。
後置 if とか 後置 unlesswhileuntil もあるよ。

let x = if x < Screen.width then x else Screen.width;   // :? タイプの if 式

let y = if y < Screen.height {
    y
} else {
    Screen.height
};                              // if っぽい if 式

if は式だし、then 節はかっこつけなくてもいいんじゃないかとかとかとか…………。

*1:Swift ではそれは val でなくて let なのだ

*2:variable で代用することもできるけど、不変更性を持つ変数の場合は? const を使いたくなかった

Uva をはじめようかなー

www.adventar.org

エントリーポイント

Uva は決まったエントリーポイントを持たない言語です。
例えば、クラスの外においては Java は一切の文あるいは式(クラス・インターフェイス定義、import 文、package 文を除く)を書くことが許されていませんが、 Uva では許されています。
なので、println( "Hello, World!" );main() の外に書いても構いません。

shared のこと

shared というのは一見キーワードのように見えますが、実のところアノテーションの 1 つです。
shared が付いた関数や変数、クラスなどはこのソースコードの外からでも見えるようになります。
つまり、可視性を司るアノテーションです。

ループ

Uva にはループは for-inwhileuntil の 3 つがあります。
for-in は以下のような、C# でいう foreach の形をもつ for 文です。

for ( auto i in 1..5 ) {
    for ( auto j in 1..j ) {
        print( "*" );
    }
    println();
}

標準出力への出力

標準出力に出力する方法の 1 つとして、println() があります。
これは、何らかの値を出力し、最後に改行を出力するという関数です。

ちなみに、最後に改行を出力しない同じような関数として print() があります。

print()println() はそれぞれ、process.stdout.print()process.stdout.println()エイリアスです。

process オブジェクト

process はビルトインの現在のプロセスを表すオブジェクトです。

    variable String? input = process.readLine();

null 許容型

process.readLine() の戻り値は String? です(おそらく)。
String? の後ろの方の ?null が入っているかもしれない String 型という意味です。
String? を別の表記(複合型表記)で表すと String|Null です。

Null 型の唯 1 つのオブジェクトが null です。

    if ( exists input ) {
        String[] tokens = input.split();
        int?[] numbers   = tokens.map( int.parse );
    }

例えば int.parse() は整数値にパースできない文字列が渡された場合、null を返します。
このようなメソッドないしは関数の戻り値型は ? 付きです。

is

ある型 Tnull 許容型 T? の値が null かどうかを確かめる 1 つの方法が is 式を使うことです。
is 式は右オペランドの値が左オペランドの型あるいは左オペランドの型を継承した型である場合、真を返します。

if-exists ガード句

もう 1 つの方法が if-exists ガード句を使う方法です。

Hoge? hoge = getHogeOrNull();

if ( exists hoge ) {
    // このブロックでは hoge は Hoge 型であることが明確になる。
    println( "it's Hoge" );
} else {
    // このブロックでは hoge が null であることが確実。
    println( "it's null" );
}

James VM のこと

Uva Advent Calendar 2016、 5 日目。

ʕ•͡ω•ʔ 今日は JS の Promise のことを調べてた。

James VM は Uva 用の VM です。
正確には、Uva 用の VM になる予定です。

Uva コンパイラ uvacコンパイルすると、Uva ソースコードは基本的には James VM 用のバイトコードを生成します。
この James VM バイトコードファイルの拡張子はとりあえず、.james にしておきましょう。

James VM にはイメージが必要です。
Uva のパッケージはイメージごとにインストールできます。(多分)

プロジェクト毎にイメージを作成することができますが、通常はイメージの管理が面倒くさいのでイメージは 1 つでいいんじゃね?
とか、イメージマジウザすぎじゃない?とか言われそうだよねとかでいい考えだと思ったんだけどダメかなーって思っている。

(๑´ڡ`๑) おしまい

Linux なデスクトップ環境でおすすめの 10 のアプリ

Uva Advent Calendar 4 日目。
今日は Linux なデスクトップ環境でおすすめのアプリを紹介していきたいと思います。

10 つ全部 EmacsVim になったりした 10 things を見たって?
大丈夫。今回は EmacsVim 以外のアプリを紹介するから。

Banshee

Banshee はよさげな音楽プレイヤー。
普段、音楽を聴くのに使ってるよ。

Audacious

AudaciousWinamp ライクな音楽プレイヤー。
いつもは Banshee を使っているんだけど、音楽ファイルを 1 つだけ聴きたいといったときや、Banshee の調子がおかしくなった時などに使うよ。

Firefox

Firefox はみんな知ってると思う。

LibreOffice

Excel ファイルは誰だって扱いたくない。そうだよね?
世の中は Excel ファイルで溢れている。
そんな時は LibreOffice Calc を起動しよう。

EoG (Eye of GNOME)

EoGGNOME の画像ビューアー。
起動?、起動は結構速いよ。うん。

llpp

PDF を読もうとしたけど、文字が表示されていない?
それは困ったね。
llpp のことを考えてみた?

VLC

知ってる人もいるかもしれないけど、VLC は動画プレイヤーだよ。
もちろん、Windows でも使えるんだ。

Krita

Krita はめっちゃいいよ。
まだあんまり使ったこと無いけど。
いいはずだよ。うん。

Wine

Windows のアプリを使いたいって?
Wine のことは知ってる?

OpenTTD

OpenTTD はゲームだよ!

紳士的振る舞いを紳士 Spec でテストする

╰( ´◔ ω ◔ `)╯ Argu を使ったテストをしようと思って、NUnit でやってみたら、FSharp.Core の 4.3.0 が必要みたいなんだ

ʕ•͡ω•ʔ Argu が FSharp.Core 4.3.0 に依存しているのかな?

╰( ´◔ ω ◔ `)╯ テスト用プロジェクトは 4.4.0 でやってるから、4.3.0 に落とせば良いのかな

さあ、Uva を始めよう

Uva Advent Calendar 2 日目。
今日は Rosetta Code の題材を使ってほにゃららら*1

配列の宣言

100 の要素を持つ bool 型の配列、doors を用意します。

variable はこの変数が可変だという意味を持つアノテーションです。
variable を付けない場合、変数は値の再設定が認められません。

variable bool[] doors = new bool[]( 100 );

値の再設定が認められないということが、変数が指している参照のことなのか、実体のことなのかはまだ決まっていません。

for 文

Uva は C 言語風の for 文を使うことができません。
代わりに for-in 文を使うことができます。

for ( int i in 0..doors.length ) {
    doors[i] = false;
}

bool 型のデフォルト値は false なので、上記の false に初期化する処理は不要です。

for ( int i in 0..doors.length ) {
    for ( int j in 0..doors.length ) {
        if ( ( j + 1 ) % ( i + 1 ) == 0 ) {
            doors[j] = ! doors[i];
        }
    }
}

標準出力への出力 その 2

println()stdout.println()エイリアスです。

println( "Passes Completed!!! Here are the results" );
println();

また、文字列リテラルに変数の値を埋め込むことができます。

for ( int i in 0..doors.length ) {
    String status = doors[i] ? "Open" : "Closed";

    println( "Door #${d + 1} ${status}" );
}

(๑´ڡ`๑) おしまい

*1:コピペだけの手抜き記事ですという意味

Uva Advent Calendar 一日目

Uva Advent Calendar 1 日目。

今日は 1 日目ということで、Uva じゃないことを書こうかと思います。
ある日、dotnet-cli をインストールしようと思ったんですが、/ がパンパンでビルド中にあぼーんしてしまいます。

これはいかん! と思って、そんなに必要じゃないパッケージを消そうと思いました。
結局のところ、~/ でビルドすることで事なきを得たんですが、その前にやったことを書いておこうと思います。

他のパッケージに必要とされていないパッケージの一覧を得る

ArchWiki によると、「孤立した、他のパッケージに必要とされていないパッケージの一覧を得る」には:

% pacman -Qdt

と打てば良いことがわかります。
これは、pacman -Q と打ったときと同じフォーマット(<パッケージ名> <バージョン>)なので、 awk{ print $1; } を使うことで、パッケージ名だけの一覧にすることができます。
シェルスクリプトにするとこんな感じです:

% pacman -Qdt | awk '{ print $1; }'

それぞれのパッケージ情報を得る

あとは、それぞれのパッケージのインストール容量を知りたいので、それぞれに yaourt -Qi <パッケージ名> したいですよね。
AWK スクリプトにすると、こんな感じです:

# gen-package-info.awk
{
    print "yaourt -Qi" $1 " > " $1 ".pacinfo"
}

gen-package-info.awk を使って、こんな風に打ってみます:

% yaourt -Qdt | awk -f gen-package-info.awk | sh

標準出力にパッケージ一覧分の yaourt -Qi ${パッケージ名} > ${パッケージ名}.pacinfo を出力し、 それを sh に標準入力として渡すことで、sh に実行してもらいます。

パッケージ名とインストール容量を抜粋する

あとは ${パッケージ名}.pacinfo を見ていけば良いのですが、多すぎると、忘れちゃいますよね((ニワトリ頭))。
なんとかして、パッケージ名とインストール容量だけを抜き出せないものでしょうか。

とりあえず、.pacinfo はこんな感じになっています:

名前                   : hiredis
バージョン             : 0.13.3-1
説明                   : Minimalistic C client library for Redis
アーキテクチャ         : x86_64
URL                    : https://github.com/redis/hiredis/
ライセンス             : BSD
グループ               : なし
提供                   : なし
依存パッケージ         : glibc
提案パッケージ         : なし
必要パッケージ         : なし
任意パッケージ         : なし
衝突パッケージ         : なし
置換パッケージ         : なし
インストール容量       : 109.00 KiB
パッケージ作成者       : Massimiliano Torromeo <massimiliano.torromeo@gmail.com>
ビルド日時             : 2015年10月12日 16時41分02秒
インストール日時       : 2016年11月08日 23時33分42秒
インストール方法       : 他のパッケージの依存関係としてインストール
インストールスクリプト : No
検証方法               : 署名

これもやっぱり、AWK で処理可能です。

$1名前 の時と インストール容量 の時に、$3 が目的の値であることがわかります。
今回は JSON に変換して、Node.JS で処理してみましょう。
JSON に変換する AWK スクリプトがこちらです:

# gen-install-size.awk
BEGIN {
    print "[";
}

{
    if ( $1 == "名前" ) {
        print "{";
        print "\"name\": \"" $3 "\",";
    }

    if ( $1 == "インストール容量" ) {
        print "\"install-size\": \"" $3 $4 "\"";
        print "},";
    }
}

END {
    print "]";
}

これを使って下のように打ってみます:

% awk -f ./gen-install-size.awk *.pacinfo > ./install-size.json

これによってできた install-size.json を以下の ranking-install-size.ls で処理すると多分できます。

# ranking-install-size.ls
require! 'package-size.json': package-size-json

compare-install-size = (left, right) ->
  left.install_size - right.install_size

package-sizes = package-size-json.map (record) ->
  name = record['name']

  install-size-text = record['install-size']
  install-size = parse-float install-size-text

  if /MiB$/.test install-size-text
    install-size = install-size * 1000

  { name: name, install_size: install-size }

for record in package-sizes.sort( compare-install-size )
  console.log "#{record.name}: #{record.install_size} KiB"

おしまい。

参考

Guile では call-with-input-file の proc 内でエラーが起きるとポートが閉じられない件

問題

あんまり問題とは言えないかもしれないが、 (proc port) 内でエラーが起きた場合、ポートを閉じる手続きが呼ばれない。
助けてドラえもん!!

とはいえ、プログラムが終了する時に全てのポートは自動的に閉じられるとかどこかに書いてあった気がするので、 そんなに問題にはならないかもしれないんだけど、Ruby で言うような File.open :

# ものすごく大雑把な File.open() の実装:
class File
  def open(path, mode = "r", perm = 066)
    f = File.new( path, mode, perm )
    yield f
  ensure
    f.close
    # 何らかのエラーが起こったとしても、ensure 節で
    # 絶対に閉じられるため、安心。
  end
end

と同じようなことをしたくなった時に困るので、考えてみたいと思う。

検証

Gauche においての call-with-input-file

試しに Gauchecall-with-input-file を見てみる:

(define-in-module scheme (call-with-input-file filename proc . flags)
  (let1 port (apply open-input-file filename flags)
    (unwind-protect (proc port)
      (when port (close-input-port port)))))

これは:

(define (call-with-input-file filename proc . flags)
  (let1 port (apply open-input-file filename flags)
    (unwind-protect (proc port)
      (when port (close-input-port port)))))

と一緒。

unwind-protect というなんかすごそうなシンタックス?で守られてる。 でも Guile にはそんなものはない。

Guile においての call-with-input-file

Guile においての call-with-input-file は 2 つある。 1 つ目は R5RS の call-with-input-file だけど:

(define* (call-with-input-file
          file proc #:key (binary #f) (encoding #f) (guess-encoding #f))
  "PROC should be a procedure of one argument, and FILE should be a
string naming a file.  The file must
already exist. These procedures call PROC
with one argument: the port obtained by opening the named file for
input or output.  If the file cannot be opened, an error is
signalled.  If the procedure returns, then the port is closed
automatically and the values yielded by the procedure are returned.
If the procedure does not return, then the port will not be closed
automatically unless it is possible to prove that the port will
never again be used for a read or write operation."
  (let ((p (open-input-file file
                            #:binary binary
                            #:encoding encoding
                            #:guess-encoding guess-encoding)))
    (call-with-values
      (lambda () (proc p))
      (lambda vals
        (close-input-port p)
        (apply values vals)))))

2 つ目は R6RScall-with-input-file で:

(define (call-with-input-file filename proc)
  (call-with-port (open-file-input-port filename) proc))

便利な call-with-port が付いて………:

(define (call-with-port port proc)
  "Call @var{proc}, passing it @var{port} and closing @var{port} upon exit of
@var{proc}.  Return the return values of @var{proc}."
  (call-with-values
      (lambda () (proc port))
    (lambda vals
      (close-port port)
      (apply values vals))))

やっぱ、ダメじゃねぇか!!!
やっぱり call-with-values 使ってんじゃねーかァァァァァァァァァァァァァァ!!!!!!!!!!!!!!!

マジでそうなの?

Guile で call-with-values を使ってるけど、本当に後の処理(ポートを閉じる処理)が呼ばれないのか検証してみた。

(define error-happen? #t)

(call-with-values
    (lambda ()
      (when error-happen?
      ;;;
      ;;; producer で起こったエラーはそのまま突き抜けてしまう。
      ;;; 
        (error "*Oops!* teleporter"))
      (values 4 5))
  (lambda (a b)
    (display "handler")
    (newline)))

上記のコードを demo-call-with-values.scm に保存して実行してみた結果が以下である:

% guile ./demo-call-with-values.scm
Backtrace:
In ice-9/boot-9.scm:
 160: 7 [catch #t #<catch-closure 1cac460> ...]
In unknown file:
   ?: 6 [apply-smob/1 #<catch-closure 1cac460>]
In ice-9/boot-9.scm:
  66: 5 [call-with-prompt prompt0 ...]
In ice-9/eval.scm:
 432: 4 [eval # #]
In ice-9/boot-9.scm:
2404: 3 [save-module-excursion #<procedure 1cce9c0 at ice-9/boot-9.scm:4051:3 ()>]
4058: 2 [#<procedure 1cce9c0 at ice-9/boot-9.scm:4051:3 ()>]
In /home/rihine/workspace/guile-cwv/./demo-call-with-values.scm:
  10: 1 [#<procedure 1d9ebc0 ()>]
In unknown file:
   ?: 0 [scm-error misc-error #f "~A" ("*Oops!* teleporter") #f]

ERROR: In procedure scm-error:
ERROR: *Oops!* teleporter

上記の通り、 handler とは出力されていない(そりゃそーだ)。 替わりにテレポーターに引っかかってしまった。

解決法みたいなもの

すごく簡単な解決方法として、 Guile で unwind-protect を実装するというものが考えられる。
あるいは、 finally のようなものを継続を使って実装する、とか。

そういえば、 catch の後にポートを閉じれば同じようなことができるはず。

バックトレースとか、スタックフレームのこと

さて、現在のスタック・トレースを得るには make-stack 関数を呼び出します。
引数の #t を忘れないようにしてください:

(define a-stack (make-stack #t))

a-stack の型をとりあえず <stack> としましょう。
<stack> オブジェクトの中には <frame> オブジェクトが幾つか含まれています。
<frame> オブジェクトを取り出すには stack-ref 関数を使います。

また、<stack> オブジェクトの中に含まれている <frame> オブジェクトの数は stack-length 関数で知ることができます。
<stack> オブジェクトの中に含まれている全ての <frame> オブジェクトをリストとして得たいということだってありますよね。
stack->frames 関数を使えば、それができます。
これは、guileソースコードの中のテストコードから取ってきたものです:

(define (stack->frames stack)
  "Return the list of frames comprising STACK."
  (unfold (lambda (i)
            (>= i (stack-length stack)))
          (lambda (i)
            (stack-ref stack i))
          1+
          0))

<frame> オブジェクトに対しては frame? だとか frame-procedure などの関数が使えます。
が、関数を呼んでいるファイルパスやら行数などはなんかとれなさそうです。

Why Guile People!!!??