ソフトウェア⼯学の知⾒を⽣かすプログラミング教育
南⼭⼤学 理⼯学部 ソフトウェア⼯学科

蜂巣吉成

⾃⼰紹介
• 名古屋⼤学 ⼯学部 情報⼯学科• 阿草先⽣の研究室• 南⼭⼤学 理⼯学部 ソフトウェア⼯学科• 担当授業 (2000-現在)• プログラミング基礎 (1年次: Haskell, C)• プログラミング応⽤ (1年次: C)• オブジェクト指向プログラミング (3年次: Java)• 2017年度より 完全クォーター制

研究テーマ• プログラミング教育⽀援 (2013-)• 授業を効率よく⾏いたい• プログラミング演習⽤プルーフリーダの試作• 蜂巣 吉成, 吉⽥ 敦, 桑原 寛明, 阿草 清滋.
• ソフトウェア⼯学の基礎 XXIV (FOSE 2017), pp. 53-62• プログラミング学習における構⽂図式を⽤いた
構⽂理解⽀援⽅法の提案• 安達 有希, 蜂巣 吉成, 吉⽥ 敦, 桑原 寛明, 阿草 清滋• 情報教育シンポジウム (SSS2018), 8 pages• プログラミング演習における構⽂要素の種類毎のビューによるコーディング状況把握⽅法の提案• ⽯元 慎太郎, 蜂巣 吉成, 吉⽥ 敦, 桑原 寛明, 阿草 清滋• 情報教育シンポジウム (SSS2018), 8 pages

プログラミング演習⽤
プルーフリーダ• プログラミング演習において• 学⽣は実⾏例通りに動作すれば良いと考えがち• 「きれいではない」プログラムを書いている場合がある• 学習者の「きれいではない」プログラムを指摘• 教員が⼿動で⾏うと⼤変なので⾃動化したい• 単なるコーディングスタイルのチェックではなく教育意図という意味的な観点からのチェックきれいなプログラムを書く修⾏プログラムを書くということは、正しい構⽂を使い、バグを直し、それなりに⾼速に動くようにすれば済むわけではない。プログラムはコンピュータだけでなくプログラマにも読まれるからだ。きれいに書かれたプログラムは汚く書かれたプログラムよりも読んで理解しやすいし、修正するのも簡単になる。きれいに書く修⾏はコードが正しく動く可能性を⾼める。さいわい
これはそれほどつらい修⾏ではない。

Brian Kernighan and Rob Pike, 福崎 俊博 訳
「プログラミング作法」 ASCII, 2000

「きれいな」プログラム を意識して書くことが技術者への⼀歩「きれいな」プログラム• ここではコーディングスタイルではなく• 課題の教育意図に合っている• 簡潔性，明瞭性，⼀般性などの観点• 学習者が課題を通して何を学ぶか理解して計算⼿順や処理内容について論理的に理解する動機: 課題 配列の平均と最⼩値8個の実数値を配列に⼊⼒し，その平均値と最⼩値を出⼒するプログラム．
以下のマクロと関数を定義して利⽤．
#define DATASIZE 8void readDoubleArray(double d[], int size)
double avgDoubleArray(double d[], int size)
double minDoubleArray(double d[], int size)
実⾏例:
input data: 56.7 85.9 45.9 98.9 88.4
74.3 66.6 53.3
avg=71.250000, min=45.900000

課題 配列の平均

double avgDoubleArray(double d[], int size)
模範解答プログラム

学習者プログラム1

int i;
double sum;
sum = 0;for (i = 0; i < size;i++)sum += d[i];return sum/size;

int i;
double sum, avg;
sum = 0;for (i = 0; i < size;i++) {
sum += d[i];avg = sum/size;}
return avg;

繰返しを⽤いた計算
繰返し前の変数の初期化
教育意図
繰返しにおいて不要な計算をしない配列の典型的な⾛査⽅法
配列の先頭要素から最後の要素まで⾛査課題 配列の最⼩値

double minDoubleArray(double d[], int size)
模範解答プログラム

学習者プログラム1

int i;
int i;
double min;double min;min = d[0];min = d[0];for (i = 1; i < size;for (i = 0; i < size;
i++)
i++)
if (d[i] < min)if (d[i] < min)min = d[i];min = d[i];return min;return min;繰返しを⽤いた計算
繰返し前の変数の初期化
繰返しにおいて不要な計算をしない配列の典型的な⾛査⽅法
配列の先頭要素を処理した後で，教育意図
次の要素から最後の要素まで⾛査課題 配列の最⼩値

double minDoubleArray(double d[], int size)
模範解答プログラム

学習者プログラム2

int i;
int i;
double min;double m;min = d[0];m = 100;for (i = 1; i < size;for (i = 0; i < size;
i++)
i++)
if (d[i] < min)if (d[i] < m)min = d[i];m = d[i];return min;return m;
繰返しを⽤いた計算
繰返し前の変数の初期化
実⾏例:
繰返しにおいて不要な計算をしない
input data: 56.7 85.9 45.9配列の典型的な⾛査⽅法
98.9 88.4 74.3 66.6 53.3
配列の先頭要素を処理した後で，
avg=71.250000,
min=45.900000
次の要素から最後の要素まで⾛査課題 配列の平均

double avgDoubleArray(double d[], int size)
模範解答プログラム

学習者プログラム2

int i;
double sum;
sum = 0;for (i = 0; i < size;i++)sum += d[i];return sum/size;

int i;
double s, a;
sum = d[0];for (i = 1; i < size;i++)s += d[i];a = s/size;
return a;

繰返しを⽤いた計算
繰返し前の変数の初期化
教育意図
繰返しにおいて不要な計算をしない配列の典型的な⾛査⽅法
配列の先頭要素から最後の要素まで⾛査課題 配列の平均と最⼩値
main 模範解答プログラム
double data[DATASIZE], avg, min;printf("input data: ");readDoubleArray(data, DATASIZE);
avg = avgDoubleArray(data, DATASIZE);
min = minDoubleArray(data, DATASIZE);

main 学習者プログラム

教育意図

関数の定義，呼出し
double data[DATASIZE], avg, min;マクロの定義，利⽤
printf("input data: ");readDoubleArray(data, 8);
avg = avgDoubleArray(data, 8);
min = minDoubleArray(data, 8);プルーフリーダの必要性• 実⾏例通りに動作し，制御構造も⼤まかには書けているが，教育意図に合わない「きれいではない」プログラム• 「なんとなく」作られた
単に実⾏例と同じ動作をするプログラム• コーディングチェッカでチェックできる?

評価基準

配列の平均

配列の先頭要素を処理した後次の要素から最後の要素まで処理

sum = d[0];for (i = 1; i < size; i++)sum = sum + d[i];配列の最⼩値

min = d[0];for (i = 1; i < size; i++)if (d[i] < min)min = d[i];⼤まかな構造は似ているが，⼀⽅は適切でもう⼀⽅は不適切

課題毎の
評価基準

プルーフリーダ• プログラムから教育意図に該当する部分を抽出(評価コード)• 学習項⽬を与えると意図パターンを適⽤して，評価コードを抽出• 繰返しや条件分岐などの制御構造をチェック• 教育者のパターン記述は不要• パターンの追加も可能(カスタマイズパターン)• 模範解答プログラムと学習者のプログラムの評価コードを⽐較

プログラム

評価コード抽出処理

学習項⽬

カスタマイズパターン

意図パターン

評価コード

学習項⽬

• 教科書の例題や演習問題から教育意図を分析• 学習単元
この3項組が学習項⽬• 条件分岐: if, switch• 繰返し: for, while, do-while• 配列: for• プログラムに現れる制御⽂を表す• 条件式の有無• 制御⽂の条件，forの繰返しの範囲• 初期化の有無• 繰返しで計算される変数の初期値

意図パターン• 学習項⽬毎にプログラムの抽出箇所(教育意図に該当する箇所)をパターン化(配列，条件式あり，初期化あり)
変数

値

配列参照

$var=$VAL OR [];for(=$VAL; $REL_OP; $OP){
$var=
関係演算⼦
演算⼦
}

意図パターンの例(繰返し，条件式あり，初期化あり)$var=$VAL;
for(=$VAL;$REL_OP;$OP){$var=}$var=$VAL;
while($REL_OP){$var=}(繰返し，条件式なし，初期化あり)$var=$VAL;
for(;;){$var=}$var=$VAL;
while(){$var=}

正規化処理• コーディングスタイルの統⼀
double x, ans = 1;
for (i = 0; i < n; i++)ans = ans * x;

複数変数の宣⾔→ 1変数の宣⾔
変数宣⾔時の初期化→ 宣⾔と代⼊⽂

double x;
double ans;
ans = 1;for (i = 0; i < n; i++) {ans = ans * x;}

制御⽂の本体の単⽂→ ブロック化など

評価コードの抽出例
配列の最⼩値
模範プログラム

min = d[0];for (i = 1; i<size; i++)if (d[i] < min)min = d[i];学習者プログラム1

min = d[0];for (i = 0; i<size; i++)if (d[i] < min)min = d[i];学習者プログラム2

m = 100;for (i = 0; i<size; i++)if (d[i] < m)m = d[i];配列と条件分岐の評価コード 学習項⽬の組合せ$var=[];
for(=1;<;++){
if(){$var=}}$var=[];
for(=0;<;++){
if(){$var=}}$var=100;for(=0;<;++){
if(){$var=}}評価コードの抽出例
配列の平均
模範プログラム

sum = 0;for (i = 0; i<size; i++)sum = sum + d[i];return sum/size;
学習者プログラム2

s = d[0];for (i = 1; i<size; i++)s = s+ d[0];a = s/size;
return a;

評価コード$var=0;for(=0;<;++){$var=}$var=[];
for(=1;<;++){$var=}

評価コードの抽出例
配列の平均
模範プログラム

sum = 0;for (i = 0; i<size; i++)sum = sum + d[i];return sum/size;
学習者プログラム1

sum = 0;for (i = 0; i<size; i++){sum = sum + d[0];avg = sum/size;}
return avg;

評価コード
$var=0;for(=0;<;++){$var=}

同じになってしまう$var=0;for(=0;<;++){$var=}カスタマイズパターン• 意図パターンのカスタマイズ機能• 抽出したい字句をパターンとして記述• 例: 配列の平均の計算における / を抽出${e1:EXPR} #/# ${e2:EXPR}• パターンの記述には TEBA を利⽤

吉⽥敦, 蜂巣吉成, 沢⽥篤史, 張漢明, 野呂昌満:
属性付き字句系列に基づくソースコード書き換え⽀援環境情報処理学会論⽂誌, Vol. 53, No. 7(2012), pp. 1832–1849.

カスタマイズパターンによる評価コードの抽出例
模範プログラム

sum = 0;for (i = 0; i<size; i++)sum = sum + d[i];return sum/size;
学習者プログラム1

sum = 0;for (i = 0; i<size; i++){sum = sum + d[0];avg = sum/size;}
return avg;

評価コード

$var=0;for(=0;<;++){
$var=
}
/
$var=0;for(=0;<;++){
$var=
/
}

プルーフリーダ
識別⼦のカウント• 字句列の出現回数カウント• 関数名・マクロ名• 構造体定義におけるメンバ型

main 模範解答プログラム
double data[DATASIZE], avg, min;readDobuleArray(data, DATASIZE);
avg = avgDoubleArray(data, DATASIZE);
min = minDoubleArray(data, DATASIZE);DATASIZE: 4

main 学習者プログラム
double data[DATASIZE], avg, min;readDoubleArray(data, 8);
avg = avgDoubleArray(data, 8);
min = minDoubleArray(data, 8);

DATASIZE: 1

プルーフリーダを⽤いた演習
による評価
• ⽬的:

• 「きれいではない」プログラムを判定できるか• 学習者が「きれいな」プログラムを書けるようになるか• 対象者: 学部3年⽣8名(C⾔語は履修済み)• 課題: 配列の平均，最⼩値を求める• ⽅法:• 学⽣が課題のプログラムを提出すると，プルーフリーダが「きれいな」プログラムかをチェックしてフィードバック• 学⽣は何度でも提出可能

プルーフリーダを⽤いた演習による評価• フィードバック機能• minDoubleArray関数の繰返しをもっとき
れいに書ける可能性があります• avgDoubleArray関数の合計の初期値をもっと適切に設定できる可能性があります• avgDoubleArray関数の平均値をもっと適切に計算できる可能性があります• マクロをもっと適切に使⽤できる可能性があります• きれいなプログラムです (指摘がないとき)

プルーフリーダを⽤いた演習
による評価
• 結果
• 8名中6名は，最初から「きれいな」プログラム• 8名中2名は，プルーフリーダの指摘を受けて「きれいな」プログラムを作成• 1名は配列の平均の計算において， すぐに合計を計算する変数の初期化忘れ 修正できた• 1名は配列の最⼩値において
min = d[0];for (i = 0; i < size; i++)if (d[i] < min)min = d[i];

修正に
時間を
要した

プルーフリーダを⽤いた演習
による評価
• 考察
• メッセージの重要性• 変数の初期化忘れ「avgDoubleArray関数の合計の初期値を
もっと適切に設定できる可能性があります」• 最⼩値の繰返しの範囲「minDoubleArray関数の繰返しをもっときれいに書ける可能性があります」

プルーフリーダを⽤いた演習による評価• プルーフリーダの評価• 「きれいな」プログラムを「きれいではない」と判定したケースはなし• 「きれいではない」プログラムを「きれい」と判定したケース• ⼊⼒を促すプロンプト(“input data: ”)をreadDoubleArray関数に記述• avgDoubleArray関数で平均を求める変数を初期化 (avg=0;)
カスタマイズパターンの追加

プルーフリーダを⽤いた演習
による評価

• 考察
• プルーフリーダでは「きれいなプログラムです」と表⽰されたが，実⾏時エラーになるという質問• main関数における配列の宣⾔
double d[] = {DATASIZE};• 要素数1の配列 (問題は要素数8の配列)• マクロの出現回数は適切• コンパイラでは指摘されない
実⾏時エラーの箇所を指摘するツールとして利⽤

プルーフリーダ まとめ• 実⽤化に向けて• 習熟度にあったわかりやすいメッセージ• 修正箇所を考えさせるメッセージ• 具体的に修正箇所を指摘したメッセージ• 複数の模範解答プログラムとの⽐較• アンチ・プログラム• 「きれいではない」プログラムと⽐較• 評価コードの⽐較によるヒント機能

⽂法理解⽀援ツール• 学⽣は意外と⽂法がわかっていない!

• 形式⾔語理論やコンパイラの授業を学ぶまでは⽂法についてあまり気にしない• コンパイラのエラーメッセージもよく理解していない

⽂法理解は抽象化能⼒• 学習者の書いたプログラムから
構⽂図式インスタンスを表⽰するツール

⽂法理解 (20年くらい前)• ⽂法の異なる複数の⾔語を学んで，⽂法やプログラミング⾔語の概念を理解
C⾔語
変数
宣⾔

Pascalint n;
double x;

var n: integer;
x: real;繰返し for(i=1;i<=n;i++)for i=1 to n do

ブロック {}；
句点 「。」

セミコロン

begin
end
読点 「、」

プログラミングと⽂法理解• トップダウン• ⽂法を理解して，それにしたがってプログラムを記述• プログラミング⾔語の概念を理解している学習者
複数の⾔語を学ぶ
⽂法の必要性
概念の抽象化• ボトムアップ

⽂法を意識させてこのプロセスを⽀援• プログラムを書きながら，⽂法を学ぶ• プログラミング⾔語を初めて学ぶ学習者

プログラミングと⽂法理解
繰返し
各⾔語の⽂法

設計指針，思想プログラム
C, Java for(i=1;i<=n;i++)Pascal for

i=1 to n do

Python for

i in range(n):Rubyfor i in 1..nScratch⽂法を理解していないと修正が難しい誤り例 1
forが空⽂の例
for (i=2; n>1;);
if (n%i == 0) {printf("%d", i);n = n/i;} elsei++;• ループ本体が空⽂• コンパイルはできるが期待した結果は得られない

そもそも ; はどこに書く?

⽂法を理解していないと修正が難しい誤り例 2
if-elseに誤りのある例
if (n%i == 0)
printf(“%d”, i);n = n/i;else
i++;gcc[1]のコンパイル結果(version 4.6)

clang[2]のコンパイル結果(version 3.9)

初級者から中級者へ
if-elseが複合⽂の例

if-elseが単⽂の例

if (n%2==0) {printf(“偶数¥n”);} else {printf(“奇数¥n”);}

if (n%2==0)
printf(“偶数¥n”);
else
printf(“奇数¥n”);

同様の実⾏結果が得られるがなぜか?
「⽂」の理解につながる
複合⽂も⽂
再帰的定義

⽂法理解⽀援ツール
⽂法，プログラムの可視化
⽂法を表す⽅法 → 構⽂図式
プログラムの可視化 → 構⽂図式インスタンス• 構⽂図式のように図で表す⽅がわかりやすい• 構⽂図式インスタンス• 構⽂図式の⾮終端記号のノードに学習者が記述したプログラムの断⽚を記述した図式• 構⽂を理解していなくても読めなんども読むことでより構⽂を理解できる

⽂法理解⽀援ツール
構⽂図式

予約語

終端記号

非終端記号

構文図式：

構⽂図式インスタンス
構⽂図式に⾃⾝のプログラムを当てはめる
while (i<=5) {printf("%d ", i);i++;}

⽂法理解⽀援ツール
想定される使⽤場⾯• コンパイルエラーを修正できない• 期待される実⾏結果を得られない• ⽂法の誤りを図として可視化• 作成したプロブラムの⽂法を確認する• 初級者から中級者へ• ⾃分の書いたプログラムを可視化することでイメージの定着を図る実際のツールによる表⽰
概略

https://akg.se.nanzan-u.ac.jp/syndia/
if ⽂，for ⽂
while ⽂に対応https://akg.se.nanzan-u.ac.jp/syndia/

for⽂が空⽂の例
for (i=2; n>1;);
if (n%i == 0) {printf("%d", i);n = n/i;} elsei++;

空⽂ (;) も⽂
本来繰り返したい⽂が空⽂
本来繰り返したい文が空文となっているhttps://akg.se.nanzan-u.ac.jp/syndia/

elseの誤りの例
if (n%i == 0)
printf(“%d”, i);n = n/i;else
i++;else の前に if がない

if のthen部は1⽂だけ

https://akg.se.nanzan-u.ac.jp/syndia/

複合⽂と単⽂if (n%2==0) {printf(“偶数¥n”);} else {printf(“奇数¥n”);
}

どちらも⽂

if (n%2==0)
printf(“偶数¥n”);
else
printf(“奇数¥n”);

https://akg.se.nanzan-u.ac.jp/syndia/

複雑な if ⽂ (3値の中央値)
2⾏⽬のif-else⽂の図式
if (x>=y)if (y>=z) /* 2⾏⽬ */m = y;
else if (x>z)
m = z;else
m = x;else
if (x>=z) /* 9⾏⽬ */m = x;
else if (y>=z)
m = z;else
m = y;

9⾏⽬のif-else⽂の図式

if – else if – else の正しい理解 (中級者向け)

演習・評価C⾔語を⼀通り学習した学部3年⽣10名を対象に構⽂理解に関する演習を⾏った
構⽂理解に関するアンケートに回答してもらった⽐較演習を⾏うために、学⽣をAとBの2グループに分けた
A グループはツールを利⽤なし
B グループはツールを利⽤あり

演習・評価アンケート結果

1.プログラミングの演習や課題を⾏う際に講義資料や教科書を⽤いるとき，最も注⽬するのはどこですか.

Aグループ

Bグループ

構⽂の説明

0⼈

1⼈

プログラム例

1⼈

3⼈

プログラムの説明

2⼈

1⼈

実⾏例、実⾏結果

2⼈

0⼈

その他

0⼈

0⼈

演習・評価アンケート結果

2.プログラミング⾔語の構⽂に対して当てはまるものはどれですか.

構⽂を理解している
構⽂を知っている
聞いたことがある
全くわからない

Aグループ
0⼈
4⼈
1⼈
0⼈

Bグループ
0⼈
4⼈
0⼈
1⼈

演習・評価アンケート結果

3. 次の while のプログラム例を構⽂図式(図上)に当てはめると，
図下のようになります.
while (i<=5) {printf("%d ", i);i++;}

この図を参考に if による条件分岐の構⽂図式と，次のプログラムを構⽂図式に当てはめた図式を書いてください.
複数の図式になっても構いません.
if (n == 0)
printf("n is 0");else if (n > 0)
printf("n is greater then 0");else
printf("n is less than 0");

演習・評価
学⽣の記述例１

学⽣の記述例２

アンケート結果
if-else if-else として認識

⽂法と制御フローを混同している

正しい構⽂図式を記述できた学⽣は1⼈もいなかった

演習・評価演習問題

4.ぶら下がりの else について
if (a > 0)
if (b > 0)
printf("X¥n");else
printf("Y¥n");4-1. 実⾏せずに a, b の正負4通りの出⼒を記述する.
4-2. 実⾏して a, b の正負4通りの出⼒を確認して，そのような結果になる理由を述べる.
4-3. 次の表の出⼒になるようにプログラムを修正する.

演習・評価演習問題

5. 素因数分解のプログラムを期待した結果になるように修正する(コンパイル，実⾏してよい).

for (i=2; n>1;);
if (n%i == 0)
printf("%d", i);n = n/i;elsei++;演習・評価演習問題結果A (ツールなし)

4-1
4-2
4-3
5

B (ツールあり)

正

誤

正

誤

3⼈
4⼈
5⼈
4⼈

2⼈
1⼈
0⼈
1⼈

3⼈
3⼈
3⼈
4⼈

2⼈
2⼈
2⼈
1⼈

• Aグループの⽅が元々⽂法の理解が深かったようである• 3 年⽣なので，今回の問題はこれまでにも経験していると考えられる• ツールに対する感想は好意的• 正しいプログラムの⽂法の確認に利⽤

⽂法理解⽀援ツール まとめ• 扱える⽂法の拡張• Scratchのようなブロックを⽤いて
⽂法表⽰

プログラミング演習におけるコーディング状況把握• 教員の側からプログラミング演習を効率的に⾏いたい• 数⼗⼈の学⽣を数⼈の指導者(教員，TA)で指導• 学⽣の躓いている箇所の把握に時間がかかる• 同じような箇所で躓いている学⽣• 2⼈いたら，もっと多くの学⽣が躓いている• コーディング状況把握の時間を短縮• 躓き箇所に合った的確な全体指導従来のアプローチ
C3PV[1] : コーディング時間，⾏数，エディタ操作数，エラー継続時間から相対的に遅れている学⽣を特定
IDISS[2] : コンパイル，インデント状況，クラスの形式的記述，
ユニットテストから判断
Bit Arrow[3] : エラー内容とエラー継続時間から作業状況を教員へ提⽰• 遅れている学⽣個⼈を特定可能
全体指導は⾏えない• コンパイル前のソースコードでは解析が難しい• 躓いている箇所については分からない[1] 井垣宏, ⿑藤俊, 井上亮⽂, 中村亮太, 楠本真⼆：
プログラミング演習における進捗状況把握のためのコーディング過程可視化システム C3PV の提案
情報処理学会論⽂誌, Vol.54No.1(2013), pp.330-339.
[2] ⻑⾕川伸, 松⽥承⼀, ⾼野⾠之, 宮川治：プログラミ ング⼊⾨教員を対象としたリアルタイム授業⽀援システム
情報処理学会論⽂誌, Vol.52 No.12(2011), pp.3135-3149.
[3] ⻑島和平, 堀越将之, ⻑慎也, 間辺広樹, 兼宗進, 並⽊美太郎：
プログラミング学習⽀援環境BitArrowの教員⽀援機能の設計と試作
情報教育シンポジウム論⽂集, 18 号 (2017), pp.129-136.

本研究のアプローチ• 編集中のソースコードを解析してコーディング状況を把握

制御⽂のビュー

• 制御構造を抽出して進捗を⼤まかに把握• 代⼊における演算⼦と型を抽出して細かく把握• 変数名の違いは型として抽象化

演算⼦と型のビュー• 抽出した結果を同値類分割でグループ化• 同じような記述をしている学⽣数がわかる• 確認すべきソースコードの種類を減らす

本研究の提案⼿法の流れ
学⽣の
ソース
コード

コーディング状況把握処理
正規
補完
化

補完
⽬的：編集中のソースコードを構⽂解析する
内容：記述途中のソースコード→波括弧の対応が合わない
TEBAで構⽂解析可能な形へプログラムを直す
波括弧の対応が合うように「}」を末尾へ加える
int power(int a,int n){
int i,ans = 0;
for(i = 0; i<b; i++)ans *= a;

int power(int a,int n){
int i,ans = 0;
for(i = 0; i<b; i++)ans *= a;
}

抽
出

同値類
分割

ビュー

TEBA
• C⾔語で記述されたソースコードの構⽂解析器• 波括弧の対応が取れていれば近似的に解析可能
対応可能なソースコード• 宣⾔の無い変数の使⽤• ;が無い式• +,=など演算⼦で終わる式

吉⽥敦, 蜂巣吉成, 沢⽥篤史, 張漢明, 野呂昌満:
属性付き 字句系列に基づくソースコード書き換え⽀援環境情報処理学会論⽂誌, Vol.53 No.7(2012), pp.1832-1849.

本研究の提案⼿法の流れ
学⽣の
ソース
コード

コーディング状況把握処理
正規
補完
化

補完
⽬的：編集中のソースコードを構⽂解析する
内容：記述途中のソースコード→波括弧の対応が合わない
TEBAで構⽂解析可能な形へプログラムを直す
波括弧の対応が合うように「}」を末尾へ加える
int power(int a,int n){
int i,ans = 0;
for(i = 0; i<b; i++)ans *= a;

int power(int a,int n){
int i,ans = 0;
for(i = 0; i<b; i++)ans *= a;
}

抽
出

同値類
分割

ビュー

正規化
⽬的：記述の違いによる分散を減らす
内容： ・a++ を a = a+1 に置換
・a += x を a = a + x に置換
・単⽂の制御⽂を複合⽂にする・コンマで区切られた初期化式を個別の式にする
int power(int a,int n){
int i;
int ans=0;
for(i = 0; i<b; i++){ans=ans*a;
}
}

抽出処理
同値類分割を効果的に⾏う計算において重要な情報を取り出す

抽出する内容• 制御⽂• リテラル• 演算⼦•型• 関数呼び出し抽出例

int power(int a,int n){
int i;
int ans=0;
for(i = 0; i<b; i++){ans=ans*a;}}

表: 型抽出の例外処理
種類
抽出後例
未定義変数 Undef
ポインタ
p_int ,
p_double

変数名の抽象化型の間違いを発⾒可能//power()
int=0
for{
int=int*int}

同値類分割
ソースコード 1
double sum(double a,double b){
double ans = a + b;
return ans;}
ソースコード 2

抽出後1//sum()
double=double+doublereturn double

演算⼦と型のビュー
抽出後2double sum(double a,double b){
double s;
s=a+b;
return s;//sum()
double=double+doublereturn double

ソースコード 3

抽出後3

double sum(double a,double b){
int ans = a + b;
return ans;
}

同値類分割

//sum()
int=double+double
return int

コード

分類数

//sum()
double=double+doublereturn double

2//sum()
int=double+double
return int

1検証• 対象：C⾔語を⼀通り学んだ学部3年⽣11⼈• WebIDEを⽤いて1分毎にデータを取得
問1

整数 x の n 乗 (n>0) を返す関数
int power(int x, int n)

問2

実数 x の n 乗を返す関数
double power(double x, int n)
秒を分，秒の形に変換してポインタ引数に格納する関数
void time(int s, int *min, int *sec)
ユークリッドの互除法により最⼤公約数を求める再帰関数
int euclid(int a,int b)
以下にユークリッドの互除法のアルゴリズムを⽰す．
euclid(a, b) = a
if b=0euclid(b,a mod b) otherwise
実数を交換する関数とそれを⽤いて要素数 n の実数配列 を逆順にする
関数
void swap(double *x, double *y)void reverse(double a[], int n)

問3
問4

問5

問題1 power関数のビュー

65

問題1 power関数のビュー
再帰関数

XOR演算⼦ ^( Haskellと混同?)

66

問題1 power関数のビュー

67

問題1 power関数のビュー

68

問題1 power関数のビュー

69

問題1 power関数のビュー

70

問題1 power関数のビュー

71

問題1 power関数のビュー

72

問題1 power関数のビュー

73

問題1 power関数のビュー

74

検証 同値類分割の分割数
演算⼦と型のビューによる分割数

制御⽂のビューによる分割数
2分 5分 10分 15分 30分
問題1
問題2
問題3
問題4
問題5
(swap)
問題5(reverse)

2
4
3
4
2

4
7
5
6
1

4
8
5
6
1

4
7
4
6
1

5
1

2

2

3

2

2

問1,5では分割数を半分以下に減らせた

2分 5分 10分 15分 30分
問題1
問題2
問題3
問題4
問題5
(swap)
問題5(reverse)

10 9
8 10
11 9
4 10
5 4

5
11
8
11
3

5
10
8
11
2

11
2

6

8

7

8

8

Swap関数などでは分割数を減らせた

制御⽂のビューでは多くの問題で教員が読むコード量を減らせた演算⼦と型のビューでは⽐較的単純な問題ならば減らせた検証 同値類分割の分割数
演算⼦と型のビューによる分割数

制御⽂のビューによる分割数
2分 5分 10分 15分 30分
問題1
問題2
問題3
問題4
問題5
(swap)
問題5(reverse)

2
4
3
4
2

4
7
5
6
1

4
8
5
6
1

4
7
4
6
1

5
1

2

2

3

2

2

2分 5分 10分 15分 30分
問題1
問題2
問題3
問題4
問題5
(swap)
問題5(reverse)

10 9
8 10
11 9
4 10
5 4

5
11
8
11
3

5
10
8
11
2

11
2

6

8

7

8

8

分割数が多い理由
問題2 : 制御⽂の記述⽅法の差のnが負の場合の処理の差など
問題4 : 再帰関数で解答している学⽣が少ない
問題5 : swap関数の呼び出しにおけるインデックスの指定⽅法の差検証 具体的な指導の観点から
問3 秒を分，秒の形に変換してポインタ引数に格納する関数
void time(int s, int *min, int *sec) {*min = s/60;*sec = s%60;}

5分 制御⽂のビュー

制御⽂のビュー
不要な制御⽂を⽤いている% 演算⼦を知らない指導

剰余算(%)の利⽤⽅法について
元のソースコードより有効であると確認

コード

If {
}
for {
}
if
while {
}

分類数

6
2
1
1
1

検証 具体的な指導の観点から演算⼦と型のビュー

コード• 制御⽂のビューで正解判定した
コードの間違いを発⾒• ポインタの理解が⾜らない学⽣を特定可能• %を⽤いた学⽣が少数であることが確認可能
指導
剰余算(%)の利⽤⽅法について
ポインタの利⽤⽅法について
正しくは*p_int//time()*p_int=int/60*p_int=int%60

分類数

2

模範解答

//time()2//time()*p_int=0
while{
int=int-60*p_int=*p_int+1}*p_int=int

1

実は何も
書いてい
なかった

//time()if{
p_int=int/60
int=int-(p_int*60)}

1//time()if

1

検証 具体的な指導の観点から演算⼦と型のビュー• 制御⽂のビューで正解判定した
コードの間違いを発⾒• ポインタの理解が⾜らない学⽣を特定可能• %を⽤いた学⽣が少数であることが確認可能
指導
剰余算(%)の利⽤⽅法について
ポインタの利⽤⽅法について

コード

分類数

//time()for{
int=int+1}

1//time()*p_int=int/60*p_int=int-(*p_int*60)

1//time()*p_int=int/60*p_int=int%60
if{
}

1

//time()int=int*p_int=int/60*p_int=int-(*p_int*60)

1コーディング状況把握 まとめ• 模範解答との⾃動⽐較
• 従来のアプローチとの統合• エディタ操作数• エラー継続時間• 実⾏情報まとめ• プログラミングはものづくりの基礎• 論理的思考• 抽象化能⼒• スマホネイティブ世代に興味がもてる教材• Scratch は楽しんでやる• 端末上のテキストの⼊出⼒は厳しい• Web上での⼊出⼒• Raspberry Pi• micro:bit

Webによる⼊出⼒
getInt(label)関数
フォームから整数を1つ読み込む
nextInt(label)関数
フォームから
次の整数を1つ読み込むRapsberry Piを使ったプログラムでLEDを制御1秒点灯，1秒消灯
0.8秒点灯，0.8秒消灯
0.6秒点灯，0.6秒消灯

0.4 秒点灯，0.4 秒消灯
0.2 秒点灯，0.2 秒消灯

initPins ();for(n=0;n<5;n++){writePin (17 ,1);delay(1000-(200*n));writePin (17 ,0);delay(1000-(200*n));}