コメントを承認制にした
昨日からスパムが来ていてウザいので、コメントを承認制にしました。
あと、https にしてみました。
標準入力に入力が詰まってるかどうかを調べるには eof を使えばいいらしい
よく、標準入力を使って:
% ps ax | grep hoge
とかやるけど、Perl6 ではどーやるんだろーと思ったので、IO::Handle を調べていたら、
&eof
を使えばいいっぽいということがわかったので、すくりぷよ書いてみた:
#!/usr/bin/perl6 # -*- mode: perl6; -*- # .. file:: sink use v6; sub MAIN() { unless $*IN.eof { say $*IN.slurp.chomp; } }
名前はてけとー。
標準入力が詰まってない場合は何もしないので、何かを入力すると何かする。
こんな風に使う:
% echo Hello, World! | ./sink
Hello, World!
MoarVM の Configure.pl を Perl6 で書こうとした話
Perl が書けなかったので、MoarVM の ./Configure.pl を Perl6 で書いて理解してみようと思った:
use v6; use Getopt::Long; constant $NAME = 'moar'; constant $GENLIST = 'build/gen.list'; sub MAIN() { my Bool $failed = False; my %args; my %defaults; my %config; get-options( %args, <help|? debug:s optimize:s instrument! coverage os=s shell=s toolchain=s compiler=s ar=s cc=s ld=s make=s has-sha has-libuv static has-libtommath has-libatomic_ops has-dyncall has-libffi pkgconfig=s build=s host=s big-endian jit! enable-jit prefix=s bindir=s libdir=s mastdir=s make-install asan ubsan valgrind telemeh> ); say $args; # pod2usage( True ) $; # say 'Welcome to MoarVM!'; }
そもそも、Perl6 の Getopt::Long
の definition が Perl の Getopt::Long
と違う可能性が微レ存だったので、いきなりそこで躓いた。
それに、Perl6 の Getopt::Long
は = が必須みたいなので、Perl の Getopt::Long
とは全然違う感じがする。
use v6; use Getopt::Long; # # Perl5 の Getopt::Long のように実装されているなら、以下が動くはず: # my $capture = Getopt::Long.new( <help|?> ); # # 今のところ、動かない………。 # say $capture;
Getopt::Tiny
というのもあるんだけど、それも真偽値は =!
が必要。
use v6; use Getopt::Tiny; my Hash $opts = {help => False, version => False}; get-options( $opts, <help=! verbose=!> ); say $opts;
ということで、Getopt::OldFashion を書くことにした。
こんな風に書けるはず:
use v6; use Getopt::OldFashion; my %opts = get-options( <help|? version verbose> ); say %opts;
すごくシンプル過ぎるビルドシステム書いた
2 日も空いちゃったけど、書いた。
いつしか加わってしまった Emacs の markdown-mode の末尾の半角スペース 2 つが 2 つのアンダーバーになってしまう
クソ現象で markdown-mode が使えないため、text-mode を使っている。
と思ったら、Emacs を再起動したところ、治った。
そんなことはともかく、こういうへろーわーるどな C 言語のソースコードをコンパイルしていきたい。
// examples/hello.c #include <stdio.h> int main() { printf( "Hello, World!" ); return 0; }
こういう Build.p6
をソースツリーのトップに置き、このファイルを叩くことで基本的にビルドを行うことができる:
# examples/Build.p6 use v6; use IO::Glob; use Build::Simple; my @sources = glob( './*.c' ); my @objects = gather for @sources -> $source { take compile $source; } link 'hello', |@objects;
&compile
や &link
は Build::Simple
モジュールに入っている。
お好みで IO::Glob
を use
するととっても便利である。
叩く時は以下のようにする:
% perl6 -I../lib ./Build.p6
-I../lib
は perl6 が lib/Build/Simple.pm6
を探しやすくするために必要である。
が、perl6/ecosystem
に入った暁には zef install Build::Simple
すれば必要なくなる。
ということで、lib/Build/Simple.pm6
は以下の通り。
今回は is export
と書くことで Build::Simple::compile
と書かなくて良くなるということがわかった。
# lib/Build/Simple.pm6 use v6; unit module Build::Simple; sub compile($dependency) is export { my $target = $dependency.extension: 'o'; say "compiling {$dependency}"; shell [ 'gcc', '-c', $dependency, '-o', $target ].join(' '); $target; } sub link($target, *@objects) is export { say "linking {$target}"; shell [ 'gcc', |@objects, '-o', $target ]; }
%*ENV<CC>
とかでコンパイラのコマンドを環境変数からとってきたりしたいなーなどと思っている。
ということで、また次回。
Perl6 の参照の話
というわけで、アドベントカレンダーを登録してないけどそんな気持ちでクソエントリを書いていこうと思う。
前もやったかもしれないけど、Perl6 の参照の話をしよう。
my @puyo = (1, 2, 3);
簡単な配列を入れた @puyo
を宣言する。
そして、それを $myon
に入れる。
my $myon = @puyo;
ここで、$myon
は何を持っているだろう?それは、@puyo
が持っている配列の参照だ。
えーと、配列の参照って?
╰( ´◔ ω ◔ `)╯ わからん
じゃあ、$myon
をまた別の配列変数に入れてみよう:
my @hunyo = $myon;
ここで @hunyo
と @puyo
は等価だろうか?
say
で中身を表示してみよう:
[[1 2 3]]
違う。
@hunyo
の中身は [[1, 2, 3]]
となぜかネストした配列になっている。
これは、$myon
には配列そのものではなく、配列のリファレンスが入っていることが原因だ((おそらくそうなんだろうなと言う意味))。
別の表現をすると、配列型の変数には配列しか設定できないため、スカラー変数の値を配列変数に設定した場合、暗黙のキャストが起こり、配列は配列の配列へと変容してしまう。
これを防ぐには、変数の前に |
(パイプ)を付ける:
my @hunyo = |$myon;
このような現象は以下のように Map の値が配列であるというような場合に起こりやすい:
my %already = ( authors => ['John Due'] ); { my @authors = do if %already<authors>:exists { %already<authors> # %already<authors> はスカラーを返す! パイプを使え! } else { [] }; say @authors; # ウボァーーーーー!!!! @authors は [['John Due']] だ!!!! }
私はJSON を扱う際にこの現象にぶち当たり、うぎゃーーーーーとなった。
みなさんも Perl6 を書く時には注意して欲しい。
なんと Perl6 ではホームディレクトリを表すチルダを変換してくれない
嘘だろ?! って思ったんだけど、ほんとっぽい。
~/.bashrc
を /home/alice/.bashrc
って変換してくれるようなバンドルされてるメソッドとか関数がないって意味ね。
# -*- mode: per6; -*- # \file: 00-childa.t use v6; use Test; plan 2; is $*HOME.add( '.bashrc' ).Str, '~/.bashrc'.IO.absolute, '.absolute でチルダを変換できるだろ、常識的に考えて……'; is $*HOME.add( '.bashrc' ).Str, '~/.bashrc'.IO.resolve , '.resolve でチルダを変換できるだろ、常識的に考えて……'; done-testing;
これを実行してみると:
% perl6 ./00-childa.t
1..2
not ok 1 - .absolute でチルダを変換できるだろ、常識的に考えて……
# Failed test '.absolute でチルダを変換できるだろ、常識的に考えて……'
# at ./00-childa.t line 8
# expected: '/home/rihine/workspace/p6-midi-converter-demo/~/.bashrc'
# got: '/home/rihine/.bashrc'
not ok 2 - .resolve でチルダを変換できるだろ、常識的に考えて……
# Failed test '.resolve でチルダを変換できるだろ、常識的に考えて……'
# at ./00-childa.t line 9
# expected: '/home/rihine/workspace/p6-midi-converter-demo/~/.bashrc'
# got: '/home/rihine/.bashrc'
# Looks like you failed 2 tests of 2
グワーーーーーーーーーーーーーーーーーーー!!!!!!
ちょー簡単な &expand-filepath
はこんな感じかもだ:
sub expand-filepath($filepath) { $filepath.subst( '~', $*HOME.Str ) }
.subst
とかで変換できるんだし、無くてよくね?って感じなのかもしれない。
NativeCall つらい
GLib とか Gtk を NativeCall で呼び出そうとしてるんだけど、つらい。
C 言語でやるような typedef
はどーやんの?
GLib とかでこーいう感じの gsize
は:
typedef unsinged long gsize;
Perl6 では以下のように constant
る。
constant gsize = uint32;
構造体ってどーすんの?
Perl6 Document に書いてあるけど、こんな風に class
として書くよ!書くよ!
class GtkApplication is repr( 'CStruct' ) { has GtkApplication $.parent; has Pointer $.priv; }
列挙体を渡したい時は?
わからん。
こんな風にやったら、ダメだった:
enum GApplicationFlags ( G_APPLICATION_FLAGS_NONE => 0 ); sub gtk_application_new(Str, GApplicationFlags) returns Pointer[GtkApplication] is native('gtk-3') { * }
is repr('CEnum')
とかできるんだろーか?
Perl6 の正規表現は不思議なんじゃなくて、自由だった
ここ を見て Perl6 のコードに移植するっていうのをやっていた。
Perl6 の正規表現が不思議すぎるなーと思って書いていたんだけど、よく読んだら自由だったらしい。
function isNumber (x) { if ( typeof x === 'number' ) return true; if ( /^0x[0-9a-f]+$/i.test( x ) ) return true; return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test( x ); }
は Perl6 で書くと多分こうなる:
sub is-number($x) { return True if $x.WHAT ~~ Numeric; return True if $x ~~ /^0x<[0..9a..fA..F]>+$/; $x ~~ /^<[-+]>?(<:digit>+)?(\.<:digit>*)?(e<[-+]>?<:digit>+)?$/; }
ʕ•͡ω•ʔ 眠い
GMake サバイバルガイド 便利そうな関数編
make
は前回の記事で十分使えるようになったんじゃないかと思います*1。
私も険しいまけふぁいるの坂を駆け上がっている途中なので、ここで紹介できる関数は 2 つしかないのですが、
ええい、たらーん!!! という方もおられると思います。
そのような方はGNU make: Functionsをご覧ください。
notdir
関数
notdir
関数は実際 basename
コマンドと一緒です。
# makefile ref0 = ./packages/FParsec.1.0.2/lib/net40-client/FParsec.dll ref1 = ./packages/FParsec.1.0.2/lib/net40-client/FParsecCS.dll target-dir = ./bin/Debug all: echo $(target-dir)/$(notdir $(ref0))
以上のファイルを makefile
や Makefile
という名前でどこか空のディレクトリに保存し、make
と打ってみてください。
以下のように表示されるはずです:
% make
echo ./bin/Debug/FParsec.dll
./bin/Debug/FParsec.dll
shell
関数
shell
関数はバッククォーテーション文字列で括ったコマンドっぽいことをするようなやつです。
shell
関数を知る前はこのように書いていましたが:
SDL2_INCLUDES = `pkg-config --cflags-only-I sdl2` SDL2_DEFINES = `pkg-config --cflags-only-other sdl2` SDL2_LIBS = `pkg-config --libs sdl2) SDL2GFX_LIBS = `pkg-config --libs SDL2_gfx`
shell
関数を使うとこのように書けます:
SDL2_INCLUDES = $(shell pkg-config --cflags-only-I sdl2) SDL2_DEFINES = $(shell pkg-config --cflags-only-other sdl2) SDL2_LIBS = $(shell pkg-config --libs sdl2) SDL2GFX_LIBS = $(shell pkg-config --libs SDL2_gfx)
バッククォーテーションで括ったところはコマンドは無くなって、例えば pkg-config --cflags-only-I sdl2
の結果が SDL2_INCLUDES
にそのまま設定されます。
ビルドしてみると、このように出力されます:
% make -B
gcc -W -Wall -O2 -D_REENTRANT -I/usr/include/SDL2 -c main.c -o main.o
main.c: 関数 ‘main’ 内:
main.c:12:14: 警告: unused parameter ‘argc’ [-Wunused-parameter]
int main(int argc, char ** argv) {
^~~~
main.c:12:28: 警告: unused parameter ‘argv’ [-Wunused-parameter]
int main(int argc, char ** argv) {
^~~~
gcc -lSDL2_gfx -lSDL2 -lSDL2 main.o -o demo-sdl2-gfx
実際にやってみたい方は ( ノ╹◡◡╹)ノ SDL2_gfx を使ったデモっぽいやつ をご覧ください。
参考
*1:多分、きっと、おそらく