読者です 読者をやめる 読者になる 読者になる

glutで画像を表示したぞ。

ppm形式(raw)の画像を用意して、それをglutで表示する。
座標とかはやってない。
高さx幅xRGBのサイズのarrayを用意して、画像を読み込み、glDrawPixelsを実行するだけ。

コンパイル方法。(オプションが多いね。)

gcc a.c -lglut -lGL

コード

#include<GL/glut.h>
#include<stdio.h>
#include<stdlib.h>

unsigned char image[400][640][3];

int ppm_read(char *filename, unsigned char *pimage){
  FILE *fp;
  if((fp=fopen(filename,"rb"))==NULL){
     printf("ERROR:%s\n",filename);
     exit(-1);
  }
  fscanf(fp,"P6\n640 480\n255\n");
  fread(pimage,sizeof(char),640*400*3,fp);
  fclose(fp);
  return 0;
}


void disp(){
    glClear(GL_COLOR_BUFFER_BIT);
    glDrawPixels(640, 400, GL_RGB, GL_UNSIGNED_BYTE, &image[0][0][0]); 
    glFlush();
}

void resize(){

}

void keyboard(unsigned char key,int x,int y){

}

void mouse(int button,int state,int x,int y){

}

void motion(int x,int y){
    
}


void init(){
    glClearColor(0.0, 0.0, 1.0, 1.0);
    ppm_read("a.ppm",&image[0][0][0]);
}

int main(int argc,char *argv[]){
    glutInit(&argc,argv);
    glutCreateWindow("TITLE NAME");
    glutInitDisplayMode(GLUT_RGBA);
 
    glutDisplayFunc(disp);
    glutKeyboardFunc(keyboard);
    glutReshapeFunc(resize);
    glutMouseFunc(mouse);
    glutMotionFunc(motion);
 
    init();
    glutMainLoop();
    return 0;
}

syntax-rulesの同じ深さの<ellipsis>

同じ深さので、違う場所で宣言された場合どうなるのか確認した。
多くの処理系では、短いほうが優先される。

(define-syntax foo
  (syntax-rules ()
    ((_ ((a ...) ...) (( b ... ) ... ))
     (quote ( ((a b) ...) ... )))))

(display 
  (foo ((a b)(c d e)) ((A B)(C D E))));(((a A) (b B)) ((c C) (d D) (e E)))

(newline)

(display
  (foo ((a b) ( c d )) ((A B)(C D E )) ));(((a A) (b B)) ((c C) (d D)))
(newline)

Schemeでライブラリを定義するぞ

Schemeのr7rsにはライブラリを定義する構文がある。
例↓

(define-library (niyarin test)
    (import (scheme base))
    (export foo1 foo2)
    (begin 
      (define (foo1 a) (+ a 1 ))
      (define (foo2 a) (- a 1))))

Schemeのライブラリ名は、ライブラリを一意に判別するため、識別子と正の整数のリストを使う。
define-libraryでは、外側の環境へアクセスできないので、使いたい機能をimportする必要があり、また外側の環境で使う場合どの識別子を見られるようにするのかexportで指定する必要がある。
あとは、begin内にlibrary本体を記述すれば最低限使えるようになると思う。
define-library内では、例が使われたものの他にinclude include-ci include-library-declarations cond-expandが使える。

作ったライブラリを使うには、もちろんimportするだけでよい。
ただし、ライブラリ自体は現在の環境から見えるようにする必要があるので、外側のファイルに書いた場合なんかは、include、loadなどする必要がある。

(import (niyarin test) (scheme write))
(display (foo1 1));2

無事使えた。
以上。

dynamic-wind。

dynamic-windについてまとめた。

dynamic-windのつかいかた

(dynamic-wind before thunk after)
  • 引数はすべてthunk(引数0の手続き)。
  • before、thunk、afterの順に実行する。
  • ただし、真ん中のthunkで継続を取り出して外側から呼び出した場合や中から外側の継続を呼び出した場合は特別な動きをする。
  • 外側から継続を呼び出した場合、呼び出す前にbefore、後にafterを呼ぶ。
  • 内側から外側の継続を呼び出した場合その継続が呼ばれる前にafterを呼ぶ。(もちろんbeforeはその前に呼ばれている)


実行例

(define cont #f)

(dynamic-wind 
  (lambda () (display 1))
  (lambda  () (display (call/cc (lambda (c) (set! cont c) 2))))
  (lambda () (display 3)(newline)))

(cont 0)

結果

123
103

やること。
beforeで1を表示、afterで3と表示して改行
真ん中のthunkで、継続を取り出してその結果を標準出力する。
そして外側から継続を呼ぶ。

raiseとraise-continuableとwith-exception-handler。

Scheme(R7RS)の例外の発生とハンドラについてまとめた。
with-exception-handerで例外ハンドラを登録し、raise、raise-continuableで例外を発生させる。
raiseとraise-continuableの違いは、handlerを呼び出した後での処理に違いがある。

with-exception-handler

(with-exception-handler handler thunk )
  • thunk内で例外が発生するとhandlerが実行される。
  • handlerは1引数の手続き。
  • thunkは名前の通り引数なし手続き。


raise-continuable

(raise-continuable obj)
  • 例外を発生させ、objをhandlerに渡す。
  • handlerが実行し終えると、例外を発生させた場所に戻る。返り値はhandlerの結果。
  • 一度handlerを呼び出して戻ってきても、再度例外を発生させれば同じhandlerが立ち上がる。


raise

(raise obj)
  • 例外を発生させ、objをhandlerに渡す。
  • handlerが実行し終えると、二度目の別の例外が発生する。
  • 二度目の例外を補足するhandlerへ渡る値は規定されていない
  • 二度目の例外のhandlerは、一つ外側のwith-exception-handerで登録されたhandlerが呼び出される

SRFI1 Constructorsを眺めたぞ。

srfi1のリストを生成するやつら。
以下は、てきとーにドキュメント眺めてまとめたもの。
cons,list,make-list,list-copyらはr7rsに含まれているので無視する。

xcons

第二引数をcar部に、第一引数をcdr部にconsセルを生成する。

(xcons 1 2)
;(2 . 1)

cons*

引数の最後が末尾cdr部になるリストを生成する。
(cons* a1 a2 … an-1 an) => (a1 a2 … an-1 . an)

(cons* 1 2 3 4)
;(1 2 3 . 4)

list-tabulate

0〜n-1のそれぞれに、初期化関数を適用したリストを返す。

(list-tabulate 5 (lambda (n) (* n n ) ) )
;(0 1 4 9 16)

circular-list

与えた引数を繰り返す循環リストを生成する。

(circular-list 1 2 3 4)
;#0=(1 2 3 4 . #0#)

iota

個数、初期値、増加値を指定して、数値のリストを生成する。
例(個数5 初期値 0 増加値 1の場合)

(iota 5 0 1)
(0 1 2 3 4)

Whitespaceを使ったぞ。

Whitespaceは、esolangの一つで、[space]、[tab]、改行だけでスタックマシン的な命令を表現する言語。
チュートリアル
これが割とあっさりしてるので、動作確認用に書いたコードをメモとして貼ることにする。
[space]はS、[tab]はT、改行はLとして表記する。

stdinで数字を読んで、stdoutで出力する。

数字をechoするだけ。

SSSL
SL
STL
TTTTTTL
STL
L
L

SSSLで、0をstackにpushする。
SLSで、stackトップを複製する。つまりstackの中身は、[0,0]。
TLTTで、数値を標準入力で読み込んで、stackトップの値のアドレスに入れる。stackトップをpopする。
TTTで、stackからアドレスを取り出して、そのアドレスにあるヒープのデータをstackにpushする。
TLSTでstackトップの値を数値として標準出力する。stackトップをpopする。
LLLでプログラム終了。

3+2をして標準出力する

SSSTTL
SSSTSL
TSSSTL
STL
L
L

SSSTTL、3をstackにpush。
SSSTSL、2をstackにpush。
TSSS、stackから2つ取り出し、加算結果をstackに積む。
TLST、stackから1つ取り出し、数値として標準出力する。
LLLでプログラム終了

long jump

L
SL
STTSSSSTL
SSSL
TL
STL
SSSTTSSSSTL
SSSTL
TL
STL
L
L

LSLSTTSSSSTL、ラベルaにジャンプする。(STTSSSST=‘a’)
SSSL、0をstackにpush。(実行されない)
TLSTL、標準出力(実行されない)
LSSSTTSSSSTL、ラベルaを定義する。
SSSTL、1をstackにpush。
TLST、標準出力。
LLL、プログラム終了。

サブルーチン

L
SL
STTSSSSTL
L
SSSTTSSSTSL
SSSTL
TL
STL
TL
L
SSSTTSSSSTL
L
STSTTSSSTSL
L
L

LSLSTTSSSSTL、ラベル'a'にロングジャンプ。
LSSSTTSSSTSL、ラベル'b'を定義
SSSTL、1をstackに積む
TLST、数値として標準出力
LTL、サブルーチンを抜けてreturnする。
LSSSTTSSSSTL、ラベル'a'を定義。
LSTSTTSSSTSL、サブルーチンとしてラベル'b'にジャンプ
LLL、プログラム終了。

入力した値が0ならば'y'をそれ以外なら'x'を標準出力する

条件分岐。

SSSL
SL
STL
TTTTTL
TSSTTSSSSTL
SSSTTTTSSSL
TL
SSL
SL
STTSSSTSL
L
SSSTTSSSSTL
SSSTTTTSSTL
TL
SSL
SSSTTSSSTSL
L
L
L

SSSL、0をstackに積む
SLS、stackトップを複製
TLTT、標準入力結果をstackトップが示すアドレスに入れる。
TTT、stackトップが示すアドレスのデータをstackトップにpushする。
LTSSTTSSSSTL、stackトップの値が0ならばラベル'a'に飛ぶ。
SSSTTTTSSSL、120(‘x’)をstackに積む。
TLSS、stackトップをcharとして出力する。
LSLSTTSSSTSL、ラベル'b'に飛ぶ。
LSSSTTSSSSTL、ラベル'a'を定義する。
SSSTTTTSSTL、121(‘y’)をstackに積む。
TLSS、stackトップをcharとして出力する。
LSSSTTSSSTSL、ラベルbを定義する。
LLLプログラム終了