コンテンツにスキップ

期末試験 2025#

答えの表示/非表示

時間:60分,問題:25問(各4点)
SCコードとJavaバイトコードの補足資料あり

1. 関数型言語#

関数型言語に関する説明のうち,適切なものを全て選択せよ#

  1. 関数型の計算モデルはラムダ計算である ✅
  2. 関数型では計算手順の列挙によりプログラムを表現する
  3. 関数型では変数への再代入は原則禁止である ✅
  4. 関数型の優れた性質は命令型プログラミングでも適用できる ✅
  5. ラムダ記法を用いることで関数名を持たない関数を表現できる ✅

Note

B. 手順の列挙ではなく関数を組み合わせる.

関数aに関する説明のうち,適切なものを全て選択せよ#

a = λxfy. + x (f 3 y)
  1. 引数x yを束縛変数,引数fを自由変数と呼ぶ
  2. 関数aはカリー化済みである
  3. 関数aは第一級オブジェクトである ✅
  4. 関数aは高階関数である ✅
  5. 関数aに対する関数適用a 10 * 2の結果は23である

Note

A. 全て束縛変数である.
B. 引数が複数あるのでカリー化されていない.
C. 関数は全て第一級オブジェクトである.
D. 引数fが関数であるため高階関数である.
E. + 10 * 3 2 = 16

ラムダ式の同値関係のうち,\(\beta\)変換に該当するものを全て選択せよ#

の前後は同値を意味する.

  1. λfx. f x 5 λgx. g x 5
  2. λfx. f x 5 λgy. g y 5
  3. (λfx. f x 5) + λx. + x 5 ✅
  4. (λfx. f x 5) + 4 + 4 5 ✅
  5. plus 4 5 (λxy. + x y) 4 5

Note

A. \(\alpha\)変換.
B. \(\alpha\)変換.
E. 名前付き関数plusの単なる展開(書き換え)であり,\(\alpha\)変換でも\(\beta\)変換でもない.

次の関数xxxに対し,適切な関数名を一つ選択せよ#

xxx = λpq. p true q

なおラムダ計算において関数truefalseは以下のように定義される.

true  = λxy. x
false = λxy. y

  1. not
  2. and
  3. or ✅
  4. xor
  5. if

Note

引数はp qの2項なので答えはand or xorのいずれかである(notは1項,ifは3項).そのうえで挙動を試してみると良い.

xxx true  true  = ???
xxx true  false = ???
xxx false true  = ???
xxx false false = ???

まずはxxx true trueを試す.

xxx true true = true
(λpq. p true q) true true
 true true true      ; pとqにtrueとtrueを適用
 (λxy. x) true true  ; 1つ目trueを展開
 true                ; λxyにtrueとtrueを適用
この時点で答えはxorではない.andorのいずれかである.

xxx true false = false
(λpq. p true q) true false
 true true false
 (λxy. x) true false
 true

答えはorに確定する.関数適用は常に左結合である点に注意すること.

あるいは関数の意味から以下のように考えても良い.

前提:
trueは2項x yを受け取り1つ目の項xを返す関数である
falseは2項x yを受け取り2つ目の項yを返す関数である
関数xxxの本文p true qに着目すると:
p=trueのときに1つ目の項trueを返す
p=falseのときに2つ目の項qを返す

まさにorの振る舞いである.

2. 論理型言語#

Prologに関する説明のうち,適切なものを全て列挙せよ#

  1. Prologは関数の組み合わせでプログラムを表現する
  2. Prologは処理手順の列挙でプログラムを表現する
  3. Prologは算術演算が可能である ✅
  4. Prologの内部処理はカリー化に基づく推論の繰り返しである
  5. Prologでは開発者によるバックトラックの制御が可能である ✅

Note

A. 関数ではなく論理式を組み合わせる.
B. 手順ではなく論理式を組み合わせる.
C. もちろん可能.講義中で示した最大公約数gcd/3の内部でも剰余演算を利用していた.
D. カリー化ではなく単一化である.
E. カット!/0や失敗fail/0などが存在する.

Prologにおける単一化操作のうち,成功するものを全て列挙せよ#

= は左辺と右辺の単一化を試みる命令である.

  1. X = 5 ✅
  2. p(X, Y) = p(5, 5) ✅
  3. p(X, Y) = ps(5, 5)
  4. p(X, Y) = p(A, B) ✅
  5. p(X, Y) = p(A, A) ✅

Note

C. 述語が異なると単一化は全て失敗する.
E. 2つの変数XYが偶然同じ値をとっても論理としては矛盾していない.

次の述語xxxに対して,最も適切な名前を一つ選択せよ#

src1.pl
xxx(0, 1).
xxx(N, T) :- Ns is N - 1, xxx(Ns, Ts), T is Ts * N.
  1. sum(総和)
  2. factorial(階乗) ✅
  3. square(二乗)
  4. sqrt(平方根)
  5. power(べき乗)

先のsrc1.plの述語定義に含まれる問題点を全て選択せよ#

  1. 述語xxxが2つの論理式で定義されており実行効率が悪い
  2. カット!/0が利用されておらず単一化が成功しない
  3. 1つ目の述語定義xxx(0, 1).の両方の項が定数であり単一化が成功しない
  4. 負の整数を与えた際に無限ループが発生する ✅
  5. バックトラックを発生させると無限ループが発生する ✅

Note

A. 論理型言語では同じ述語を複数の論理式によって定義する.
B. 意味不明である.
C. 意味不明である.
D. ✅ ?- xxx(-1, X).を実行すると負の方向に発散する.
E. ✅ ?- xxx(4, X).で解24を得た後にバックトラックを発生させる,つまり別解を探索させると負の方向に発散する.

3. C言語#

C言語のメモリ領域に関する説明のうち,適切なものを全て選択せよ#

  1. 機械語命令列はプログラム領域に格納される ✅
  2. 局所変数はスタック領域に格納される ✅
  3. プログラムカウンタはプログラム領域内を指し示している ✅
  4. スタックポインタはスタック領域内を指し示している ✅
  5. ベースポインタはスタック領域内を指し示している ✅

次のSCコードに関する説明のうち,適切なものを全て選択せよ#

SC言語ソースコード
int g;
void main() {
    int i;
}
左と等価なSCコード
IBS 1
ENT 3
HLT
  1. IBS 1は変数i用セルの確保である
  2. IBS 1は変数g用セルの確保である ✅
  3. ENT 3の3の内訳は制御領域セル3つである
  4. ENT 3の3の内訳は制御領域セル2つと返り値用セル1つである
  5. ENT 3の3の内訳は制御領域セル2つと変数i用セル1つである ✅

Note

A. 変数セルはENTで確保される.
B. ✅
C. 制御領域は常に2つである.
D. 返り値セルはENTとは関係なく確保されている.
E. ✅

次のSCコードの???として,適切なものを一つ選択せよ#

変数iのSCコードでの相対番地は3とする.

SC言語ソースコード
void main() {
    int i;
    i = 10 + (20 * 30);
}
左と等価なSCコード
ENT 3
???
HLT

A.

LDC 10
BOP +
LDC 20
BOP *
LDC 30
STL 3

B.

LDC 20
BOP *
LDC 30
BOP +
LDC 10
STL 3

C.

BOP +
LDC 10
BOP *
LDC 20
LDC 30
STL 3

D. ✅

LDC 10
LDC 20
LDC 30
BOP *
BOP +
STL 3

E.

LDC 30
LDC 20
LDC 10
BOP *
BOP +
STL 3

Note

逆ポーランド記法で並べれば正解である.

A. BOP +の時点で想定外の挙動が発生する.中間記法である.
B. BOP *の時点で想定外の挙動が発生する.
C. BOP +の時点で想定外の挙動が発生する.ポーランド記法である.
D. ✅ 逆ポーランド記法である.
E. 逆ポーランド記法だが被演算子の順序がおかしい.実行は可能だが結果は正しくない.610 (= 10 + 20 * 30)ではなく230 (= 10 * 20 + 30)となる.

次のSCコードの???として,適切なものを一つ選択せよ#

変数iのSCコードでの相対番地は3とする.

SC言語ソースコード
void main() {
    int i;
    while (i) { i = 0; }
}
左と等価なSCコード
   ENT 3
   ???
y: HLT

A.

x: LDL 3
   JPZ x
   LDC 0
   STL 3
   JMP y

B. ✅

x: LDL 3
   JPZ y
   LDC 0
   STL 3
   JMP x

C.

x: LDC 0
   JPZ y
   LDL 3
   STL 3
   JMP x

D.

x: LDC 0
   STL 3
   LDL 3
   JPZ x
   JMP y

E.

x: LDC 0
   STL 3
   LDL 3
   JPZ y
   JMP x

Note

以下の流れである.下向きの条件付きジャンプと上向き無条件ジャンプが交差する.

  • iをロードして条件付きジャンプ(LDC 3JPZ y),
  • while本文を実行して(LDC 0STL 3),
  • 冒頭へ無条件ジャンプ(JMP x

スタックフレームに関する説明のうち,適切なものを全て選択せよ#

  1. main関数はスタックフレームを持たない特殊な関数である
  2. 関数呼び出しの度に新たなスタックフレームが生成される ✅
  3. 関数リターンの度に新たなスタックフレームが生成される
  4. 代入処理の度に新たなスタックフレームが生成される
  5. スタックフレームの生成はプログラムカウンタの増加で実現される

Note

A. main関数もスタックフレームを持つ.
B. ✅
C. 関数終了時はフレームは消える.
D. 代入はフレーム生成とは関係がない.
E. プログラムカウンタの増加は機械語の1実行と同じ意味である.

関数呼び出しに関するSCコードの説明のうち,適切なものを全て選択せよ#

★1が関数呼び出しを実現するSCコードである.LDC MST CUPの組み合わせで実現される.変数iのSCコードでの相対番地は3とする.

SC言語ソースコード(src2.sc)
int f() {
    return 10;
}
void main() {
    int i;
    i = f();
}
左と等価なSCコード(src2.scc)
   ENT 3
   LDC x  // ★1
   MST    // ★1
   CUP 0  // ★1
   STL 3
   HLT
x: ENT 2
   LDC 10
   STL 0  // ★2
   RET 0  // ★2
  1. LDCによって呼び出し先関数fへ渡す引数を記録する
  2. MSTによって呼び出し先関数fを終了した後の戻りアドレスを記録する
  3. MSTによって呼び出し先関数fを終了した後のベースポインタを記録する ✅
  4. CUPによって呼び出し先関数f用のスタックフレームを生成する ✅
  5. CUPによって呼び出し先関数fへプログラムカウンタを指定させる ✅

Note

A. LDCが記録する情報は呼び出し先関数のアドレスである.

関数のリターンに関するSCコードの説明のうち,適切なものを全て選択せよ#

先のSC言語ソースコードsrc2.scとSCコードsrc2.sccを参照せよ.★2が関数のリターンを実現するSCコードである.STL RETの組み合わせで実現される.

  1. STLによって返り値用のメモリセルに返り値を記録する ✅
  2. 返り値は呼び出し元関数mainのスタックフレームの最下部に記録される
  3. RETによって呼び出し元関数mainへプログラムカウンタを指定させる ✅
  4. RETによって実行中関数fのスタックフレームを破棄する ✅
  5. 実行中関数fの返り値がない場合はRETは不要である

Note

A. ✅ スタック最下部が返り値用の領域である.
B. 返り値は最上部に記録される.
C. ✅
D. ✅
E. 返り値の有無に関わらずRETは必須である.

4. Java言語#

次のJavaプログラムのの時点で,スタック領域中の局所変数領域が取り得るレイアウトを一つ選択せよ#

はメモリの単一セルを表す.値EB..はメモリへの参照である.

void main() {
    Integer i = new Integer(1);
    int a[] = {2, 3};
    String s = new String("h");
    // ★

A. h 3 2 1

B. EB1C 3 2 1

C. EB1C EB14 EB0C 1

D. EB1C EB14 EB0C EB04

E. ✅   EB1C EB0C EB04

Note

3つの参照型変数があるので,スタックには3つの参照が積み上げられる.

次のJavaプログラムのメソッドf()を実行した際の現象として,最も適切なものを一つ選択せよ#

void f() {
    int i;
    i = 10 + f();
}
  1. Null参照の例外が発生する
  2. スタック領域不足の例外が発生する ✅
  3. ヒープ領域不足の例外が発生する
  4. メソッド領域不足の例外が発生する
  5. 例外は発生せず無限にプログラムが実行され続ける

Note

再帰呼び出しによりスタックフレームが無限に積み上げられる.最終的にスタック側の不足が発生する.

次のJavaプログラムのメソッドf()を実行した際の現象として,最も適切なものを一つ選択せよ#

void f() {
    List<String> list = new ArrayList<>();
    while (true) { list.add("a"); }
}
  1. Null参照の例外が発生する
  2. スタック領域不足の例外が発生する
  3. ヒープ領域不足の例外が発生する ✅
  4. メソッド領域不足の例外が発生する
  5. 例外は発生せず無限にプログラムが実行され続ける

Note

可変長配列へのaddにより,ヒープ側で不足が発生する.

次のバイトコードの???として,適切なものを一つ選択せよ#

変数iのバイトコードでの相対番地は3とする.

Javaソースコード
void f() {
    int i;
    i = 10 + (20 * 30);
}
左と等価なバイトコード
???
return

A.

bipush 10
iadd
bipush 20
imul
bipush 30
istore 3

B.

bipush 20
imul
bipush 30
iadd
bipush 10
istore 3

C.

iadd
bipush 10
imul
bipush 20
bipush 30
istore 3

D. ✅

bipush 10
bipush 20
bipush 30
imul
iadd
istore 3

E.

bipush 30
bipush 20
bipush 10
imul
iadd
istore 3

Note

SCコードの問題と全く同じである.

次のバイトコードの???として,適切なものを一つ選択せよ#

変数xbのバイトコードでの相対番地は,それぞれ3と4とする.

Javaソースコード
boolean b;
b = (10 < x);
左と等価なバイトコード
    bipush    10
    iload     3
    ???       L1
    iconst    1
    goto      L2
L1: iconst    0
L2: istore    4
  1. if_icmpge ✅
  2. if_icmpgt
  3. if_icmple
  4. if_icmplt
  5. goto

Note

条件が反転する点に注意せよ.

問題に誤りがありました(2026/02/06追記)

補足資料(と講義資料)のミスです.条件分岐系JVMコードの2項が逆でした.例えばif_icmpeq Lの正誤は次の通り.

- if(stack[optop]==stack[optop-1]){...}
+ if(stack[optop-1]==stack[optop]){...}

全員正解としておきます.

Javaのメソッド呼び出しに関するバイトコードの説明のうち,適切なものを全て選択せよ#

Javaソースコード
new X();
左と等価なバイトコード
new X
dup
invokespecial X
  1. new XによってクラスX用のメモリをヒープ領域に確保する ✅
  2. new XによってクラスX用のメモリをスタック領域に確保する
  3. invokespecial Xによって実行中関数のスタックフレームの形状を退避させる ✅
  4. invokespecial XによってXのコンストラクタのスタックフレームを生成する ✅
  5. invokespecial XによってXのコンストラクタへプログラムカウンタを移動させる ✅

プログラム実行をスタックフレームで表現する理由として,適切なものを全て選択せよ#

  1. 各メソッドの局所変数に対する自動的な初期化が可能である
  2. 同一メソッドであっても呼び出しごとに独立した局所変数領域を確保できる ✅
  3. 不要となったヒープ領域の自動的な回収が可能である
  4. 再帰呼び出し時の各メソッドの状態の保持と復元が可能である ✅
  5. 式や代入に対する自動的な型チェックが可能である

Note

A. 初期化は無関係である.実際C言語の変数は未初期化である.
B. ✅
C. ヒープの自動回収はガーベージコレクションの問題であり,フレームとは無関係である.
D. ✅
E. 型チェックは無関係である.

5. 講義全体#

プログラミング言語に関する説明として,適切なものを全て選択せよ#

  1. プログラミング言語の目的はプログラミング労力の軽減である ✅
  2. プログラミング言語を用いることで機械語よりも高い抽象度で記述ができる ✅
  3. プログラミングパラダイムとはプログラミング言語が採用する規範や哲学である ✅
  4. プログラミング言語の構文規則は文脈自由文法やBNF等を用いて定義される ✅
  5. 機械語の直接記述と比較すると,プログラミング言語を利用することで数多くの制約が発生する ✅

次のテーマについて自身の考えを理由と共に記述せよ#

「複数のプログラミング言語を学ぶべきか?」

マークシート表面の記入欄Aに解答を記述せよ.

講義全体に対する改善案や感想,意見を記述せよ#

マークシート裏面の記入欄Bに解答を記述せよ.


試験問題に対する異議申立がある場合は,マークシート裏面の空欄に記述すること.


補足資料#

SCコード命令一覧#

stack[]:スタック領域,sp:スタックポインタ,bs:ベースポインタ,pc:プログラムカウンタ

# SCコード 意味 スタック領域とレジスタに対する操作
1 LDC a load constant sp++;t[sp]=a;
2 LDL a load local sp++;t[sp]=t[bs+a];
3 LDG a load global sp++;t[sp]=t[a];
4 LDLA a load local addr sp++;t[sp]=bs+a;
5 LDGA a load global addr sp++;t[sp]=a;
6 STL a store local t[bs+a]=t[sp];sp--;
7 STG a store global t[a]=t[sp];sp--;
8 STI store at indexed addr t[t[sp-1]]=t[sp];sp--;sp--;
9 IND a indexed fetch t[sp]=t[t[sp]+a];
10 JMP L jump pc=L;
11 JPZ L jump if zero if(t[sp]==0){pc=L;}sp--;
12 ENT a enter block sp=bs+a;
13 MST mark stack sp++;t[sp]=bs;sp++;
14 CUP a※1 call user procedure t[sp-a]=pc+1;bs=sp-a-2;pc=t[bs];
15 RET a※2 return from function sp=bs-a;pc=t[bs+2];bs=t[bs+1];
16 BOP o※3 binary operation sp--;t[sp]=(t[sp])o(t[sp+1]);
17 UOP o※4 unary operation t[sp]=o(t[sp]);
18 IBS a increment base bs+=a;
19 HLT halt

※1 aは呼び出す関数の引数の数
※2 関数の返り値がvoid型のときa=1,int型の時a=0
※3 oは二項演算子(+ - * / > >= < <= ==等)
※4 oは単項演算子(+ - !等)

Javaバイトコード命令一覧#

stack[]:スタック領域,heap[]:ヒープ領域,
optop:スタック最上部を示すレジスタ,frame:制御領域を示すレジスタ,
vars:スタック最下部を示すレジスタ,pc:プログラムカウンタレジスタ

# 命令 スタック領域とレジスタに対する操作
1 bipush c optop++;stack[optop]=c;
2 ldc a optop++;stack[optop]=heap[a];
3 iload a optop++;stack[optop]=stack[vars+a];
4 aload a optop++;stack[optop]=stack[vars+a];
5 istore a stack[vars+a]=stack[optop];optop--;
6 astore a stack[vars+a]=stack[optop];optop--;
7 iadd optop--;stack[optop]=stack[optop]+stack[optop+1];
8 imul optop--;stack[optop]=stack[optop]*stack[optop+1];
9 goto L pc=L;
10 if_icmpne L if(stack[optop-1]!=stack[optop]){optop-=2;pc=L}
11 if_icmpeq L if(stack[optop-1]==stack[optop]){optop-=2;pc=L}
12 if_icmpge L if(stack[optop-1]>=stack[optop]){optop-=2;pc=L}
13 if_icmpgt L if(stack[optop-1]> stack[optop]){optop-=2;pc=L}
14 if_icmple L if(stack[optop-1]<=stack[optop]){optop-=2;pc=L}
15 if_icmplt L if(stack[optop-1]< stack[optop]){optop-=2;pc=L}
16 new X optop++;stack[optop]=malloc(X);
17 dup optop++;stack[optop]=stack[optop-1];
18 invokespecial X
※1
stack[optop+v+1]=pc;
stack[optop+v+2]=vars;
stack[optop+v+3]=frame;
vars =optop-p;
frame=optop+v+1;
optop=optop+v+4;
pc=X;

※1 vはクラスXのコンストラクタの局所変数の数,pはコンストラクタの引数の数