sbclでスクリプト
実行方法
- RubyとかPerlみたいにファイルの先頭に#!/usr/bin/sbcl --scriptを付けて,実行権限を与えてシェルから実行.#!xxxはシェバンっていうらしい.sbcl 1.0.22から使えるようになった..ただし,~/.sbclrcを読み込まないので注意.
- scriptオプションを与えて実行する.
$ sbcl --script hoge.lisp
コマンドライン引数
$ sbcl hoge foo bar
* *posix-argv* #=> ("sbcl" "hoge" "foo" "bar")
エントリポイント
- ファイルの上から順に実行されるようだ.
- mainという名前の関数が実行される,みたいなことは”無い”
tcl/tk
GUIは使わないのですぐ忘れる.tkを使う最低限のメモをまとめておく.
tclメモ
- データは全て文字列として扱われる.数値の場合は読み込まれてから数値に自動で変換される.
- 行単位のプログラミング言語で,先頭の文字列がコマンド(butonとか)
- 変数はsetで値を代入,値を取り出すときは$を付ける.
- 四則演算はexpr(expression式って意味かな?)の後に数式.Bashみたいだ.
- コマンドで置換する場合は[]で囲む.
- ウィジットの名前は"."で始める.これは"."メインウィジットを指すから.Tcl/tkのウィジット名はパス付きの名前みたいなイメージ.
set x [expr 2 + 3] # 変数とコマンド置換の例 set y "hoge [expr 2 + 3]" # 文字列内でもコマンド置換される button .b -text "hoge" # オプションは'-'で指定するUnix形式
各ウィジットのメモ
全体
- イベントとコマンドを結びつけるにはbindコマンド.
bind .widget-name event-name command
パッケージ
- 基本的にpackerを使う.グリッドの場合のみgridderを使う.placerは使わない.
- ウィンドウに対して余白を埋めるには-fillオプション.x,yで縦横を指定する.
ボタン
- 重要なのはcommandオプション.おした時のコマンドを指定できる.
- ボタンおした時,離した時でコマンド指定したい場合はbindで各イベントとコマンドを結ぶ.
ラベル(label)
- 文字列の表示
- textavailableオプションが重要.ここに変数を指定しておくと,変数の値に変わると自動でアップデートしてくれる.
# buton/labelの例 #! /usr/bin/wish # label widget set buffer "hello world!" label .l -textvariable buffer pack .l # procedure for below button command proc push_button {n} { global buffer set buffer "I push $n button." } # button widget foreach i {0 1 2 3} { button .b$i -text "button $i" -command "push_button $i" pack .b$i } # bind example bind .b0 <ButtonPress> {puts "hello tcl/tk"} # exec command button .b4 -text "pwd" -command "exec pwd &" pack .b4
Canvas
- 図形を描画できるウィジット.
- create object-type coordinate [option]形式
- line : 直線,2点(多点でも可)を指定する.
- rectangle: 長方形,左上と右下の2点を指定.
- oval : 円,外接する長方形の左上と右下の2点を指定.
- text : 文字列
- image : 図
- 共有オプション
- fillで塗りつぶし,outlineで枠の色,widthで枠の太さ
- 図形へのコマンド
- move :図形を動かせる
- bind :図形へのイベントとコマンドをバインド
- coords :図形の座標を取得
- バインドでは図形のIDだけでなくタグで指定できる.
# create canvas widget canvas .c0 -width 200 -height 150 pack .c0 # create a rectangle, and name it as r0. set r0 [.c0 create rectangle 10 10 20 20 -fill cyan] # move r0 (above rectangle) by mouse # B1-Motion: left click, and its coordinates can be access by %x and %y. # .c0 bind $r0 <B1-Motion> { set x1 [expr %x-5] set x2 [expr %x+5] set y1 [expr %y-5] set y2 [expr %y+5] .c0 coords current $x1 $y1 $x2 $y2 } .c0 bind hoge <B1-Motion> { set x1 [expr %x-5] set x2 [expr %x+5] set y1 [expr %y-5] set y2 [expr %y+5] # current means currently pointed object .c0 coords current $x1 $y1 $x2 $y2 } # create rectangle with tag .c0 create rectangle 20 10 30 20 -fill red -tags hoge .c0 create rectangle 30 10 40 20 -fill blue -tags hoge .c0 create rectangle 40 10 50 20 -fill green -tags hoge
C/C++のライブラリについて
概要
- 静的ライブラリ,動的ライブラリ(そんなものは無い.それは一般的には共有ライブラリ),共有ライブラリ,静的リンク,動的リンクなど,分かっているようで理解が曖昧な点を整理しておく.
- C++ではヘッダファイルだけで提供するライブラリがある.ヘッダオンリーライブラリなどと呼ばれているようだ.それも簡単なメモを書いておく.
静的ライブラリと共有ライブラリ
- 拡張子はそれぞれ,静的ライブラリは.a,共有ライブラリは.so.
- コンパイル時の-lで指定するのは静的ライブラリも共有ライブラリも同じ.例えば,-lmはmathライブラリがリンクされる.ただし,デフォルトでは,まず共有ライブラリ(libhoge.so)を探して,見つかれば共有ライブラリがリンクされる.共有ライブラリがなければ,静的ライブラリ(libhoge.a)を探してリンクする.明示的に静的ライブラリをリンクしたい場合は,-staticオプションを付ける.
- 静的ライブラリは単純には複数のオブジェクトファイルをまとめたもの.arコマンドで生成する.
- 共有ライブラリは各オブジェクト生成時に-fPIC(Position Independent Code)を指定して,ライブラリ生成時に-sharedを指定する.
静的リンクと動的リンク
- 静的リンクはライブラリがオブジェクトファイルに埋め込まれる.よって,ライブラリが無い環境でも動くバイナリを生成できる.しかし,ファイルサイズは大きくなる.
- 反対に,動的リンクはオブジェクトファイルにはライブラリ自体は埋め込まれず,そこに埋め込めという情報だけが埋め込まれる.これは,ファイルサイズは小さくなるものの,ライブラリが無い環境では動かない.マシン上の複数のプログラムが同じ共有ライブラリを使う場合にもメモリの削減になる.libhoge.dllはダイナミックリンクライブラリであり,名前の通り実行時にリンクされる.
- 共有ライブラリのリンクの際にライブラリを探索するパスはLD_LIBRARY_PATHである.共有ライブラリを使う場合は,このパスに対象のディレクトリが含まれていることを確認する.
- 動的リンクは実行時に配置されるので,配置に依存しないコンパイル(PIC)をしておく
具体例
- 自分のユーティリティライブラリ(myutil)を作る場合
/////////////////////////////////////////////////////////// // myutil.h void hello_world(void); /////////////////////////////////////////////////////////// // myutil.c #include <stdio.h> #include "myutil.h" void hello_world(void) { printf("Hello world!! I'm a static library\n"); } /////////////////////////////////////////////////////////// // main.c #include <stdio.h> #include "myutil.h" int main() { hello_world(); return 0; }
静的ライブラリとして使う場合
- オブジェクトファイルを普通に作って,arでまとめて,リンクする.
$ gcc myutil.c -c # myutil.oを生成 $ ar r libmyutil myutil.o # libmyutil.aを生成 $ gcc main.c -L ./ -l myutil # libmyutil.aをリンクしてコンパイル (libmyutil.soがない前提) $ gcc -static main.c -L ./ -l myutil # libmyutil.aをリンクしてコンパイル (libmyutil.soがある場合は明示)
共有ライブラリとして使う場合
- -fPICで位置非依存なコードを作って,sharedでリンク
$ gcc -fPIC myutil.c -c # myutil.oを生成 $ gcc -shared -o libmyutil.so myutil.o # libmyutil.soを生成 $ gcc main.c -L ./ -l myutil # libmyutil.soをリンクしてコンパイル
ヘッダオンリーライブラリ
common lispプログラミング
Common lispの勉強をしていて出会ったことを書き連ねていく.
コーディングスタイル
- 詳細を知りたい場合はGoogle Common lisp Coding規約を読む.
- ファイル名はhoge-foo.lispという感じでハイフンでつなぐ。
- グローバル変数は"hoge"
- 定数は+hoge+
- 大文字と小文字の違いはない。プログラムは小文字で書くことがほとんど。
- 複数行のコメントは#| ... |#.
シンボルとキーワード
- アトムはシンボルとシンボル以外に分類される。
- +, sin, xなどはシンボルである。シンボルは評価するとシンボルが指すものに評価される。シンボル自身が欲しい場合はクォートする.
- キーワードとは,評価すると自分自身になる特集なシンボル。
- リーダは:で始まるシンボルを見ると,シンボルを登録する際にそのシンボルが指すものとして,そのシンボル自身を登録する。
format
- ~がCで言う%(指示詞)
- ~%は改行。~5%は5行改行。
- ~Dは整数(大文字Dでも小文字dでも変わらない)
- ~Aは文字列でも数値でも良きにやる.
- 指示子には個数を設定できる.~10tは10カラム分のタブ.
- ~{...~}は1つのリストを消費して,リスト内の各要素を~{...~}の中の指示子で消費していく.
- print-prettyで改行とかを良きにやってくれる.
;; ~{...~}の例 (format t "~{~d ~d ~d~} ~a" '(1 2 3) :hoge)
print/princ/prin1/pprint
- prin1 : readし易い形。
- print : 一度改行を出力してからprint1。
- princ : 人向け(エスケープ文字出さないとか)
- pprint: printを読みやすくしたもの(連続スペースを1つに変換)
print1-to-string/write-to-string
- xxx-to-stringはストリームじゃなくて,文字列として出力する。
- writeはprin1よりも原始的で,print1はwriteの一部のオプションが設定されたもの。
破壊的代入
- INCF, DECF, PUSH, POPなどは破壊的な代入操作をする.モディファイアマクロと呼ぶらしい.
- 便利なマクロとして,x,yの値をswapするマクロ:ROTATEFがある.PUSH,POPも破壊的.
(defvar *x* 1) (defvar *y* 2) (incf *x*) ; (setf *x* (+ 1 *x*)) (decf *x*) ; (setf *x* (- 1 *x*)) (incf *x* 10) ; (setf *x* (+ 10 *x*)) (rotatef *x* *y*) ; swap(x,y)
defunの変数は値渡し
- defunで渡される引数は値渡し(ポインタじゃなくて,参照している値が渡される).
- だから,当然だけど変数を引数で渡して,defunの中でその変数を更新するようなことは間違い.
- ポインタ(consセル)を指している変数を渡すと,それはそのポインタのアドレスを渡していることになるから,その先の変数を変更できる.
;; 間違い (defparameter *x* 0) (defun hoge (x)) (setf x 1)) (hoge *x*) ; *x*は更新されない (setf *x* (hoge)) ; 更新したければ返り値を使う (defparameter *lst* '(1 2 3 4 5)) (defun foo (lst) (setf (car lst) 0)) (foo lst) ; (0 2 3 4 5) 更新される
リスト処理
- consやappendは元のリストは破壊しないので注意.
- リストに要素を追加したい場合は,明示的にconsしたものをsetfするか,PUSHを使う(取り出すときはPOP) .
- sortすると元の配列が壊される.しかもsortされる訳でもない?サイズが変わったりもしてる.なぜ?
- リストをシフトしたいときはalexandriaのrotate.引数で右も左もシフトできる.
(setf lst (loop for x from 0 to 100 collect x)) (sort lst #'>) lst ; ソートされたものの一部が入っている?
Array
- 可変長配列(:adjustableを指定すれば)
- make-arrayで作って,arefで参照して,setf+arefで代入する。
;; 配列生成 (defparameter *arr* (make-array 3)) ; => #(0 0 0) (aref *arr* 0) ; => 0 ;; 初期値指定 (defparameter *arr* (make-array 3 :initial-element 5)) ; => #(5 5 5)
VectorとArray
- VECTORは固定長,ARRAYは可変長であり,多次元も可能.
- 可変長なARRAYを作りたい場合は,:fill-pointerと:adjustableを設定する.:fill-pointerは現在の末尾の要素番号を指す整数.可変長のARRAYに要素を追加する場合はvector-push-extendを使う(vector-pushじゃない)
- VECTORにもmapを適用できる.(map 'vector #'func array)形式.
(defparameter *varr* (make-array 0 :fill-pointer 0 :adjustable t)) (vector-push-extend 0 *varr*) ; *varr* = #(0) (vector-push-extend 1 *varr*) ; *varr* = #(1 0) (vector-push-extend 2 *varr*) ; *varr* = #(2 1 0) (map 'vector #'print *varr*)
ハッシュ
- 文字列をKeyにするときは,:test 'equalを指定する.デフォルトはeqlになってシンボルを想定している.
- loopでループする時は独特なので覚えてしまう.
(defparameter *hash* (make-hash-table :test 'equal)) (setf (gethash "hoge" *hash*) 1) (setf (gethash "hogehoge" *hash*) 2) (setf (gethash "hogehogehoge" *hash*) 3) (gethash "hogehoge" *hash*) (loop for k being the hash-key in *hash* collect k) (loop for v being the hash-value in *hash* collect v)
setfとsetqの違い
- setqよりもsetfの方が賢い.
- setqは第一引数を必ずQuateする.
- setfは第一引数が必要なら評価して,不要なら評価しない.
eltとarefとnthの違い
- eltはシーケンス全般に使えるが,nthはリスト,arefはアレイ(ベクター)のみに使える.文字列もベクタなのでarefが使える.
- eltは汎用なので型チェックが動的に行われるが,nth/arefはそれが無いのでnth/arefの方が速い.
- 速度差があると言われているけれど,sbclだと違いわからなかった.測定ミス???
defvarとdefparameterの違い
- 使い方の違いとして、defvarはプログラムによる変数の操作、defparameterはプログラムに対する変数の操作。
- defvarは既に定義済みの変数の場合には再定義しない。
- defparameterは定義済みの有無に関わらず再定義する。
- defparameterは初期値が必ず必要.初期値が無い場合は:unboundを与える.
nullとendpの違い
- nullはnilの時にTで,それ以外は文字列でもアトムでもFNILを返す.
- endpはリスト専用で,アトムや文字列を入れるとエラーを発生する.
funcallとapply
- funcallは引数をリストにまとめて渡す、applyはリストを渡す。
(funcall #'+ 2 3 4) (apply #'+ '(2 3 4)
リーダマクロ
- (quote (1 2 3)) -> '(1 2 3)
- (function hoge) -> #'hoge これは関数名から関数の本体を得る方法。つまり関数のシンボルhogeが指すものを得る。
- "#+","#-"は処理系依存コードなどを書く目的で,#+は式が真のときに読み込まえて,#-は偽の時に読み込まれる。どんな処理系でも処理系のシンボルは定義されている。例えばSBCLなら:sbclが定義されている.これを使って,処理系によって分岐するコードは下記になる. 例) #+:sbcl (expr for sbcl case) #+:ccl (expr for clozure cl case) #-(or :sbcl :ccl) (expr unknown case)
- Vector/StructはそれとReaderがわかるようにprintでもサフィックスがつく。printで出力して,それをreadで読みこめば,そのままベクタ,構造体として扱える.
- vector: #(1,2,3)
- struct: #S(struct-name (:slot-name ...)* )
functionが不要?
- 下のコードが動くのはなんで?"#"は要らないの?
(defun square (x) (* x x)) (mapcar #'square '(1 2 3 4 5)) ; (1 4 9 16 25) (mapcar 'square '(1 2 3 4 5)) ; (1 4 9 16 25) なんで???
ループ
do系
- doは複雑なように見えて実はCのforと大きくは変わらない.初期値;更新式;終了条件;ループ本体,って感じだ.
;; リストでのループ (dolist (v '(1 2 3 4 5 6 7 8 9)) (print v)) ;; 指定回数のループ (dotimes (i 10) (print i)) ;; do ;; (do ((変数, 初期値, 更新式)*) ;; (終了判定式 返り値) ;; (body*))
loopマクロ
;; loop in list (loop for x in '(a b c d e) do (print x)) ;; with-when (loop for d in '(0 1 2 3 4 5 6 7 8 9) when (evenp d) do (print d)) ;; with if (loop for d in '(0 1 2 3 4 5 6 7 8 9) if (evenp d) do (print d)) ;; with while (loop for d in '(0 1 2 3 4 5 6 7 8 9) while (< d 5) do (print d)) ;; Counting (loop for x from 0 to 10 by 2 do (print x)) ;; repeat (loop repeat 5 do (print 'hoge)) ;; assign and update (loop for x1 = 0 then x2 for x2 = 1 then (+ x1 x2) while (< x1 30) do (print x1)) ;; iota (loop for x from 0 to 10 collect x) (setf lst '(1 2 3 4 5)) (loop for x in lst for y in (cdr lst) do (format t "x:~d y:~d~%" x y))
#'(lambda ...)の意味
- lambda式は関数オブジェクトを返すから,#'は不要なんじゃないの?と思うけど,下のような例が見られる.
(mapcar #'(lambda (x) (* 2 x)) '(1 2 3 4 5)) (mapcar (lambda (x) (* 2 x)) '(1 2 3 4 5)) ; これじゃダメ?
これを評価してみると上の式でも下の式でも同じようになる. Webを見ていると,lambda自身がマクロで,(function (lambda ... ))に展開される.すでに#'されている場合と違う場合で展開を分けているのかな?
構造体(defstruct)
- Cで言うところのstructというよりもclassに近い。
- コンストラクタとアクセッサが自動で生成される。
- コンストラクタ:make-<struct_name>
- アクセッサ :<struct_name>-<slot_name>
- 述語 :<struct_name>-p
(defstruct my-struct (slot1 nil) ; (スロット名 初期値) (slot2 nil)) (setf x (make-my-struct)) ; コンストラクタ (my-struct-slot1 x) ; アクセッサ
ディレクトリ操作
- カレントディレクトリの確認 (truname "./") #=> 処理系非依存 (sb-posix:getwd)
- カレントディレクトリ移動(loadでのデフォルトパスネームは変わらない) (ccl::cd #p"/hoge/hoge/") # CCL (sb-posix:chdir #p"directory-path")
- デフォルトのパスネームを変える (setf default-pathname-defaults #p"hogehoge")
乱数
- common lispでの乱数の生成方法.
- 一様乱数はrandで生成できる.
- Alexandriaにはリストをシャッフルしたり正規分布からの抽出などの高機能なものもある。
- TODO: 正規分布などの確率分布からのサンプリング方法
(alexandria:shuffle '(1 2 3 4 5)) ; 破壊的操作なので注意
リストからのランダム選択
(nth (random (length lst)) lst)
一様乱数
- 仕様で規定されているようだけど,実装は規定されていない.
- (random N) -> N以下の数をランダムに出力。Nが整数なら整数,少数なら少数を出力する。
- 乱数はrandom-stateに従って生成される。random-stateは常に更新され続けるので,乱数の種を渡すときはrandom-stateを設定すればいい。(make-random-state state)で設定。
- 各処理系の実装は下記
- CCLでメルセンヌ・ツイスタを使いたい場合などは,mt19937パッケージを使う。CMUCLの実装をポータブルにしたものらしい。 (ql:quickload :mt19937) (mt19937 N)で同じことができる。
多値
- 多値を返す時はvalues,それをmultiple-value-bindで受け取る.
- 多値はリストじゃないことに注意.
- リストで受け取りたいときはmultiple-value-listで受け取る.
- multiple-value-bindでバインドされる変数のスコープはmultiple-value-bindの中だけなことに注意.
文字列の数値への変換
(parse-integer "3") (read-from-string "3.0")
ファイル
- openは使わずにwith-open-fileでやる.
- printとreadの対称性を使う.printはS式をreadが読み込み得る形式で書き出す.
;; 書込テンプレート (defun save-file (filename) (with-open-file (fout filename :direction :output :if-exists :supersede) (with-standard-io-syntax (print *data* fout))))
行ごとの処理
- 行毎に処理したい場合はwrite-lineとread-lineの対称性を使う.
- read-lineは改行までを読み込んで,改行文字を削除した文字列を返す.
- read-lineは多値を返す関数で,2つ目に終端が改行文字で終わってない場合はtを,改行文字の場合はnilを返す.
- 第2引数がファイル末尾に達した時にエラーを返すか否か.デフォルトがtになっているので,nilを入れてエラー発生しないようにする.
;; file (with-open-file (fout "./tmp.dat" :direction :output) (write-line "hogehoge" fout) (write-line "foo" fout) (write-line "bar" fout)) (with-open-file (fin "./tmp.dat" :direction :input) (loop for line = (read-line fin nil) while line do (format t "~s~%" line)))
ファイルのロード(load)
- loadすると、そのファイルを読み込んでトップレベルを評価する。
; hogeディレクトリのfoo.lispをロードする (load "hoge/foo.lisp")
インデックスのセットの要素をリストから除去する
C++の場合
リストvがあって,そのリストから除去したいインデックスの集合remove_id_set(3,5,2)とかで指定する要素番号の要素を除去したい.eraseすると要素番号が一つずれるので,それも考慮して一旦集合をソートして,前から順に削除していくという方法.
#include <vector> #include <random> #include <algorithm> #include <iostream> std::random_device rnd; std::mt19937 mt(rnd()); std::vector<int> generateUniqueRandomIntegers(size_t max, int num_random_number) { std::vector<int> v(max); std::iota(v.begin(), v.end(), 0); std::shuffle(v.begin(), v.end(), mt); v.erase(v.begin()+num_random_number, v.end()); return v; } std::vector<int> eraseSetOfIndices(std::vector<int> &erase_set, std::vector<int> &v) { std::sort(erase_set.begin(), erase_set.end()); int i=0; for (auto index : erase_set) { auto it = v.begin(); v.erase(it+index-i); ++i; } return v; } int main() { std::vector<int> v(10); std::iota(v.begin(), v.end(), 0); // remove index std::vector<int> remove_id_set = generateUniqueRandomIntegers(9,3); for (auto d : remove_id_set) std::cout << d; std::cout << " are removed" <<std::endl; std::vector<int> ret = eraseSetOfIndices(remove_id_set, v); for (auto d : ret) std::cout << d; std::cout << std::endl; return 0; }
N以下の重複しない乱数(整数)の生成
C++
良い方法かは分からないけどiotaやeraseを使った例を見つけたのでメモ.やり方は一旦iotaで[0,1,...max]の配列を作って,それをシャッフルして,eraseで必要ない部分を消す.eraseはイテレータを2つ渡すと,開始位置から終了位置の1つ前までを消去できる.
#include <random> #include <vector> std::random_device rnd; std::mt19937 mt(rnd()); std::vector<int> generateUniqueRandomIntegers(size_t max, int num) { std::vector<int> v(max+1); // CAUTION "max+1" !! std::iota(v.begin(), v.end(), 0); std::shuffle(v.begin(), v.end(), mt); v.erase(v.begin()+num, v.end()); return v; }
Python
同じやり方をpythonでやる場合.sampleでシーケンスからの重複しないサンプリングが出来るのでそれを使う.
import random def generateUniqueRandomNumbers(max, num): return random.sample(xrange(max+1), num)
C/C++からPythonを使う
要旨
- Pythonには色々な便利なライブラリがある.C/C++にも便利なライブラリがある.それらが重複しない場合は一方からもう一方を叩けると嬉しい.
- 参考URL 1. Embedding Python in Another Application — Python 3.5.2 documentation
コンパイル方法
- 色々なライブラリやインクルードパスを指定する必要がある.それらを確認するためのツールがpython-config.MacOSだとframework pythonとかで一式いけるみたいだ.全部は要らない場合もあるから,確認してからCMakeList.txt作るのも良いかもしれない.
gcc hoge.cpp $(python-config --cflags) $(python-config --labs)
使い方(1)
- Py_Initialize()でPythonインタプリタを初期化.
- PyRun_SimpleStirng(pythonコード文字列)でpythonコードを実行する.
- Py_Finalize()でインタプリタを終了.
#include <cstdio> #include <Python.h> int main(void) { Py_Initialize(); PyRun_SimpleString("print 'hello world'"); Py_Finalize(); return 0; }
使い方(2)
上の例は実際はあまり使わない.使い方としては既存のスクリプトの中の関数を実行するというシーンが多いはず.これはC->Pythonの例だけど,Python->Cの例も実はほとんど変わらない.やっていることは(1)データの変換,(2)外部での実行,そして(3)データの逆変換.C->PythonとPython->Cでは順番が逆なだけ. 1.
その他メモ
- pythonとcどっちがトップ(どっちからどっち叩くんだろう?)なんだろう?
- topはmainモジュール