490 likes | 586 Views
情報処理 III www による情報発信とサービス提供 II. 第 4 回 2014 年 10 月 16 日 大塚 智宏. 本日の予定. 前回までの課題 について Ruby の基礎(4) 配列 ハッシュ 今回の課題. 前回 までの 課題について. 第 2 回課題 採点結果の返却を行いましたので,ダウンロードして確認 してください 再提出の指示があった人は, 10/22 ( 水)までに 再提出 してください それまで に再提出がない場合,今回の採点結果で確定と なります 目立った問題点 文字 コードが 「 UTF-8N 」 になっていない
E N D
情報処理IIIwwwによる情報発信とサービス提供II情報処理IIIwwwによる情報発信とサービス提供II 第4回 2014年10月16日 大塚 智宏
本日の予定 • 前回までの課題について • Rubyの基礎(4) • 配列 • ハッシュ • 今回の課題
前回までの課題について • 第2回課題 • 採点結果の返却を行いましたので,ダウンロードして確認してください • 再提出の指示があった人は,10/22(水)までに再提出してください • それまでに再提出がない場合,今回の採点結果で確定となります • 目立った問題点 • 文字コードが 「UTF-8N」 になっていない • 詳しくは第2回スライドの16枚目を参照 • 日本語の文字列を使っているのに文字コードの指定がない • 詳しくは第2回スライドの56枚目を参照 • 第3回課題 • 明日 10/17(金) 23:59締切です
プログラムの基本的な構成要素 • 値 (数値,文字列など) • 情報の記憶 (変数) • 入出力 • 制御構造 • 順次実行,条件分岐,ループ • サブルーチン,関数 • データ構造 • 配列,リスト,スタック,キュー,ハッシュ,木,… • アルゴリズム • 探索,整列(ソート),数値計算,データ解析,最適化,…
配列 (array) 複数のデータを並べておき,番号で参照できるようにしたもの 順序に意味がある 同じデータを集めたものでも,順序が違えば異なる配列になる Rubyでは各データを ,(カンマ)で区切って []で囲む カンマの前後の空白はあってもなくてもよい 複数の型のデータが混ざっていてもOK [1, 2, 3, 4] # 4つの数値からなる配列 [3, 1, 4, 2] # 上記とは異なる配列 ['Ada', 'BCPL', 'C++'] # 3つの文字列からなる配列 ["Fred", 6, "Mary", 2.5] # 数値と文字列が混ざった配列 [] # 空の配列 [a, 5, b] # 変数を含む配列 [x+1, x-1] # 式を含む配列 [1, [2, 3, 4], [5, 6]] # 入れ子(ネスト)になった配列
配列の利用(1) 変数への代入 変数に配列を代入して保持することができる 個々の要素の参照 変数名[要素番号]で個々の要素を参照できる 要素番号 (「インデックス」 「添え字」 とも言う) は0から始まる整数 numbers = [3, 1, 4, 2] strings = ['Ada', 'BCPL', 'C++'] a = ["Fred", 6, "Mary", 2.5] z = [] a[0] # "Fred" a[1] # 6 a[2] # "Mary" a[3] # 2.5 a[4] # nil(範囲外) "Fred" 0 6 1 "Mary" 2 2.5 3
配列の利用(2) 負の要素番号の指定 範囲の指定 指定された範囲のデータを含む新しい配列を返す a = [1, 4, 9, 16, 25] a[-1] # 25(最後の要素) a[-2] # 16(最後から2番目の要素) a[-6] # nil(最後から6番目の要素 =範囲外) a[0, 2] # [1, 4](a[0]から2個分) a[-2, 2] # [16, 25](a[-2]から2個分) a[-2, 3] # [16, 25](3個目はないので2個になる) a[2, 0] # [](a[2]から0個分) a[5, 2] # [](a[5]から2個 =範囲外) a[0..2] # [1, 4, 9](a[0]から a[2] まで) a[0...2] # [1, 4](a[0]から a[2]の1個前まで) a[-3..-2] # [9, 16](a[-3]から a[-2]まで)
配列の利用(3) 要素への代入 各要素には,変数と同じようにデータを代入できる 末尾よりも後ろに代入すると,配列が自動的に拡張される 範囲を指定して別の配列を要素ごとに代入することもできる 左辺と右辺で要素数が異なる場合は,自動的に拡張・縮小される a = [1, 2] a[0] = 5 # [5, 2]になる a[4] = 9 # [5, 2, nil, nil, 9]になる a[-6] = 10 # エラー (先頭よりも前の要素への代入) a[0, 3] = [6, 7, 8] # [6, 7, 8, nil, 9]になる a[-3, 3] = [5, 4] # [6, 7, 5, 4]になる a[2, 1] = [1, 2, 3] # [6, 7, 1, 2, 3, 4]になる a[0, 2] = [] # [1, 2, 3, 4]になる a[2, 0] = [5, 6] # [1, 2, 5, 6, 3, 4]になる a[1..3] = [7, 8, 9] # [1, 7, 8, 9, 3, 4]になる a[-3..-1] = [5] # [1, 7, 8, 5]になる
配列の利用(4) 要素番号の指定方法 変数や式を使って要素番号を指定することもできる 小数点以下は切り捨てられる 要素数(サイズ)の取得 fib = [0, 1] for i in 2..99 do fib[i] = fib[i-1] + fib[i-2] end arr = [1, 2, 3, 4] arr[0.5] = 5 # arr[0]に 5 を代入 arr[10.0/3.0] = 7 # arr[3]に 7 を代入 arr = [1, 2, 3] x = arr.length # xは 3 になる y = [].size # yは 0 になる (sizeはlengthの別名)
for制御構造 配列の個々の要素を順に処理できる 配列の要素を順番に変数に代入しながら,ブロック内の処理を繰り返し実行 (ループの一種) 一般形 例 配列の代わりに, 0..9等の 「範囲」 と呼ばれるデータや,後述のハッシュ等も使用できる for 変数in 配列等do 処理 ... end arr = [1, 4, 9, 16, 25] for n in arr do puts n # 配列 arrの要素を順に表示 end
eachメソッド for制御構造と同様,配列の要素を順に処理 機能はforと全く同じだが,こちらの方が好まれる傾向あり 一般形 例1 例2 配列等.each do |変数| 処理 ... end arr = [1, 4, 9, 16, 25] arr.each do |n| puts n # 配列 arrの要素を順に表示 end (0..99).each do |i| puts i # 0から99までの整数を順に表示 end
配列の操作(1): push/popメソッド 末尾の要素の追加・削除 push: 配列の末尾に要素を追加する pop: 配列の末尾から要素を取り除き,それを返す arr = [] arr.push(3) # arrは [3]になる arr << 2 # 別の書き方(arrは [3, 2]になる) arr.push(5, 6) # [3, 2, 5, 6]になる arr << 1 << 2 # [3, 2, 5, 6, 1, 2]になる arr.push([7, 8]) # [3, 2, 5, 6, 1, 2, [7, 8]] arr = [1, 2, 3, 4] x = arr.pop # xは 4,arrは [1, 2, 3]になる arr.pop # [1, 2]になる (値は捨てられる) y = arr.pop(2) # yは [1, 2],arrは []になる z = arr.pop # zは nilに,arrは []のまま w = arr.pop(2) # wは []に,arrは []のまま
配列の操作(2): shift/unshiftメソッド 先頭の要素を操作し,残りの要素を前後にずらす shift: 配列の先頭から要素を取り除き,それを返す unshift: 配列の先頭に要素を追加する arr = [1, 2, 3, 4] x = arr.shift # xは 1,arrは [2, 3, 4]になる arr.shift # [3, 4]になる (値は捨てられる) y = arr.shift(2) # yは [3, 4],arrは []になる z = arr.shift # zは nilに,arrは []のまま w = arr.shift(2) # wは []に,arrは []のまま arr = [] arr.unshift(3) # arrは [3]になる arr[0, 0] = [2] # 同じ操作(arrは [2, 3]になる) arr.unshift(5, 6) # [5, 6, 2, 3]になる arr[0, 0] = [1, 2] # [1, 2, 5, 6, 2, 3]になる arr.unshift([7, 8]) # [[7, 8], 1, 2, 5, 6, 2, 3]
配列の操作(3): 連結,反復 +: 配列同士を連結した新しい配列を返す *: 配列を指定回数だけ反復した新しい配列を返す arr = [1, 2] + [3, 4] # [1, 2, 3, 4] arr = [0] + arr # [0, 1, 2, 3, 4] arr += [5, 6] # [0, 1, 2, 3, 4, 5, 6] arr = arr + 7 # エラー (右辺が配列でない) arr = [0, 1] * 2 # [0, 1, 0, 1] arr *= 2 # [0, 1, 0, 1, 0, 1, 0, 1]
配列の操作(4): reverseメソッド 要素を逆順に並べた新しい配列を返す 配列の値を直接書き換えるわけではない arr = [2, 4, 1, 3] rev = arr.reverse # revは [3, 1, 4, 2]になる seq = (1..4).to_a.reverse # seqは [4, 3, 2, 1]になる # to_aは範囲指定を配列に変換するメソッド (後述) (0..10).to_a.reverse.each do |i| puts "count: #{i}" # 10から0まで順に表示 end arr.reverse # 値は捨てられてしまう arr = arr.reverse # arrの中身を逆順にする arr.reverse! # 直接書き換える(破壊的な)バージョンもある
配列の操作(5): sortメソッド 要素を昇順に並べ替えた新しい配列を返す reverseと同様,配列の値を直接書き換えるわけではない arr = [2, 4, 1, 3] sorted = arr.sort # sortedは [1, 2, 3, 4]になる # 文字列は辞書順にソートされる a = ["a", "B", "A", "b"].sort # ["A", "B", "a", "b"] # 数値と文字列が混ざった配列をソートしようとするとエラーとなる # (正確には,比較演算が定義されているデータ同士でないとNG) arr = [1, "a", 2, "b"] sorted = arr.sort# エラー(数値と文字列は比較できない) arr.sort # 値は捨てられてしまう arr = arr.sort # arrの中身をソートする arr.sort! # 破壊的バージョンのsort
配列の操作(6): joinメソッド 各要素を文字列に変換して結合した文字列を返す 要素間に挟む文字列を引数で指定できる arr = [1, 2, 3, 4] x = arr.join("::") # xは "1::2::3::4"になる y = arr.join # "1234"(引数を省略するとそのまま結合) arr2 = ["x", "y", "z"] str = arr2.join(' + ') # "x + y + z" z = ["a", "", "b"].join('-') # "a--b" w = [].join(':') # ""(空文字列)
プログラム例 入力を終わりまで読み込み,最後の行から順に表示する input = [] print "> " while line = gets do input << line # 読み込んだ文字列を配列の末尾に追加 print "> " end # この時点で,inputは読み込んだ各行を要素とする配列になっている # 逆順にした配列の各要素を順に表示 for s in input.reverse do print s end
実行例 • 前のスライドのプログラムを実行 • 入力終了を表すために最後に Ctrl-Z を入力
Tips: 範囲 0..9 の形式で先頭と末尾の値を指定したもの 配列ではなく,「範囲」 と呼ばれるデータ to_aメソッドで配列に変換できる 1..4 # 1以上4以下 1...4 # 1以上4未満 1.5..4.5 # 1.5以上4.5以下 'a'..'z' # "a"から "z"までの文字列 5..0 # 無効な範囲 (減少方向には働かない) m..n # mとnの値で範囲が決まる (1..4).to_a # [1, 2, 3, 4](括弧は省略不可) (1...4).to_a # [1, 2, 3] (1.5..4.5).to_a # エラー (小数には使えない) (5..0).to_a # [](範囲が無効) ('a'..'z').to_a # ["a", "b", "c", …, "z"] ("00".."99").to_a # 2桁整数の文字列 ("05","84"等)
Tips: 配列要素の表示(1) ダブルクォート文字列中では,配列の要素も式展開できる puts "#{arr[3]}" # arr[3]の内容を表示 # a[0]から a[9]までを順に表示 for i in 0..9 do puts "#{arr[i]}" end # 以下のように書いても同じ arr[0..9].each do |x| puts x end # 実はこのようにも書ける (次のスライドを参照) puts arr[0..9]
Tips: 配列要素の表示(2) 配列全体を式展開した場合は,文字列に変換されて表示される printとputsに引数として配列を与えた場合,挙動が異なる printは式展開と同じ putsでは各要素が順にputsされる (each等の代用になる) arr = [1, 2, 3] puts "#{arr}" # [1, 2, 3]と表示 arr = [1, 2, 3] print arr # [1, 2, 3]と表示 puts arr # 1(改行)2(改行)3(改行) と表示 arr.each do |x| # puts arrと同じことを eachで puts x end
配列を返すサブルーチン サブルーチンは,配列を返すこともできる # フィボナッチ数列を返すサブルーチン def fibonacci(n) fib = [0, 1] for i in 2..n do fib[i] = fib[i-1] + fib[i-2] end fib # fibの値(配列)を戻り値とする end # 0~50番目までのフィボナッチ数を表示 arr = fibonacci(50) # 配列を戻り値として受け取る puts arr
配列を引数とするサブルーチン サブルーチンの引数として配列を取ることもできる # 配列要素(数値)の平均値を求めるサブルーチン def average(a) # 配列を引数として受け取る sum = 0.0 a.each do |x| sum += x end sum / a.size # 平均値を戻り値とする end # 5つの数値の平均値を求める arr = [1.5, 4.3, 8.7, 6.2, 0.9] puts average(arr) # 配列を引数としてサブルーチンを呼び出す
Tips: 配列のコピー 配列の内容は,別の変数に代入しただけではコピーされない これは,Rubyの変数がすべて実体をもたない 「参照」 であることに起因しているが,詳しい話は少々難しくなるのでここでは省略 配列の内容をコピーしたい場合は,dupメソッドを用いる a = [1, 2, 3] b = a # aを bにコピーしたつもり a[0] = 5 # aの要素を変更すると… print a # [5, 2, 3]と表示 print b # [5, 2, 3]と表示(bも変更されている) a = [1, 2, 3] b = a.dup # aと同内容の配列を新たに作成し,bとする a[0] = 5 # aの要素を変更すると… print a # [5, 2, 3]と表示 print b# [1, 2, 3]と表示(bは変更されない)
Tips: 多重代入 複数の変数に対して一度に代入操作を行うことができる 左辺の方が数が少ない場合,余った値は捨てられる 右辺の方が数が少ない場合,余った変数は (元々何か値が入っていたとしても) nil になる 右辺の計算は代入前に行われるので,値の交換が簡単に実現できる a, b, c = 5, 6, 7 # a = 5; b = 6; c = 7と同じ a, b, c = [5, 6, 7] # 同上 a, b = 5, 6, 7 # a = 5; b = 6と同じ(7は捨てられる) a, b = [5, 6, 7] # 同上 a = 5, 6 # a= [5, 6]と同じ (多重代入ではない) a = [5, 6] # 同上 a, = 5, 6 # a = 5 と同じ (6は捨てられる) a, = [5, 6] # 同上 a, b, c = 5, 6 # cは nilになる a, b, c = [5, 6] # 同上 a, b = b, a # aと bの値を交換
練習問題 • 9枚前のスライド (「プログラム例」) のプログラムを,逆順ではなく辞書順に並べ替えて表示するように改造し,さらに,行ごとに表示するのではなく,1行に並べて表示するようにせよ。 • 入力として自然数を読み込み,その数の約数すべてを小さい順に並べて表示するプログラムを作成せよ。その際,自然数を引数とし,約数を要素とする配列を返すサブルーチンを定義してそれを利用すること。 • ただし,結果の表示はサブルーチン内では行わないこと。
ハッシュ (hash) 「キー」 と 「キーに対応するデータ」 の組の集合を格納しておくためのデータ構造 配列と違い,「キー」 には整数以外のデータ (文字列等) も使うことができる 名前(キー)を使ってそれに結び付けられたデータを参照することができる キーワードと,それに対応する値(データ)を格納して参照できる簡易データベースとして使うことができる 別の言い方をすれば,あるデータの集合を,別のデータの集合に結び付けるもの(写像)とも考えられる キー 'betty' 'bar' '2.5' 'foo' 'wilma' "bye\n" "hello" 12.4 1.72e30 35 値
ハッシュの表記 キーと対応するデータ (「値」 とも呼ばれる) を =>で連結したものを ,(カンマ)で区切り,全体を波括弧 {}で囲む キーにも複数の型を混在させることができるが,使いにくいのであまりそういうことはしない # 3つの要素を持つハッシュ # 数値をキーとし,文字列をデータとする { 1 => "one", 2 => "two", 3 => "three" } # 5つの要素を持つハッシュ # 文字列をキーとし,データの型は複数種使われている { 'foo' => 35, 'bar' => 12.4, '2.5' => "hello", 'wilma' => 1.72e30, 'betty' => "bye\n", # カンマが最後にあってもよい }
ハッシュの利用(1) 変数への代入 変数にハッシュを代入して保持することができる 個々の要素の参照 変数名[キー]で個々の要素(データ)を参照できる 配列と違って要素に順序はなく,「先頭」 や 「末尾」 などの概念も存在しない num_pairs = {3=>1, 2=>5, 4=>3, 1=>5} numbers = {"one"=>1, "two"=>2, "three"=>3} z = {} # 空のハッシュ numbers["one"] # 1 numbers["two"] # 2 numbers["three"] # 3 numbers["four"] # nil(キーが存在しない)
ハッシュの利用(2) 要素への代入 配列と同様,要素を指定してデータを直接代入できる 存在しないキーを指定すると,ハッシュは自動的に拡張される 存在するキーを指定した場合は,データが上書きされる 要素を削除するにはdeleteメソッドを使う hs = {} hs["one"] = 1 # hsは {"one"=>1}になる hs["two"] = 2 # {"one"=>1, "two"=>2} hs["one"] = "ichi" # {"one"=>"ichi", "two"=>2} hs["two"] = "ni" # {"one"=>"ichi", "two"=>"ni"} # 削除にはdeleteメソッドを使う (削除したデータを返す) hs.delete("two") # {"one"=>"ichi"}になる a = hs.delete("one") # {}になる(aは "ichi"になる) a = hs.delete("two") # {}のまま (aは nilになる)
ハッシュの利用(3) データのデフォルト値を指定 ハッシュを作成する際,存在しないキーを参照した際に得られるデータを指定しておくことができる キーの指定方法 変数や式を使ってキーを指定することもできる h1 = {} h1["one"] += 1 # エラー(h1["one"]が nil) h2 = Hash.new(0) # デフォルト値を0とする空のハッシュ h2["two"] += 1 # OK (h1["two"]は 0) count = Hash.new(0) while word = gets do word = word.chomp count[word] += 1 # 文字列の出現回数をカウント end
ハッシュの利用(4) 要素数(サイズ)の取得 ハッシュ使用時の注意 キーは重複できない 存在するキーの要素にデータを代入すると,上書きされる キーが異なっていれば,データは重複していても問題ない データからキーを参照することはできない キー ⇒ データ の一方通行 要素間に順序はない 実装によってはeachメソッド等の結果がデータを追加した順になる場合があるが,それを前提としたプログラムを書いてはいけない hs = {"one"=>1, "two"=>2, "three"=>3} x = hs.length # xは 3 y = {}.size # yは 0 (sizeはlengthの別名)
for制御構造/eachメソッド 配列と同様,ハッシュの個々の要素を順に処理できる 取り出される要素の順序は不定 hs = {"A"=>3, "B"=>5, "C"=>2} for k, v in hs do # キー,データのペアを1つずつ取り出し, puts "#{k} => #{v}" # それを表示 end hs.each do |k, v| # 同じことをeachで実現 puts "#{k} => #{v}" end hs.each do |pair| # ペアを配列として取り出すこともできる puts "#{pair}" # ["A", 3]や ["B", 5]などと表示 end
each_key/each_valueメソッド キーのみ,データのみを順に処理する やはり,取り出される順序は不定 each_value で取り出されるデータは重複するものも含む hs = {"A"=>3, "B"=>5, "C"=>2} hs.each_key do |k| # キーを1つずつ取り出す puts "#{k} => #{hs[k]}" end hs.each_value do |v| # データを1つずつ取り出す puts v # データからキーの参照は不可能 end
keys/valuesメソッド ハッシュの全キー,全データの配列を返す 要素の順序は不定のため,特定の順序で処理したい場合はsort等で並べ替えてから使う valuesが返す配列は重複するデータも含む hs = {"A"=>3, "B"=>5, "C"=>2} ks = hs.keys # ["A", "B", "C"](ただし順不同) vs = hs.values # [3, 5, 2](ただし順不同) ks.sort.each do |k| # キーの配列をソートしてから取り出す puts "#{k} => #{hs[k]}" end vs.sort.each do |v| # データの配列をソートしてから取り出す puts v end
key?/value?メソッド ハッシュにキーやデータが存在するかどうかを調べる 存在すれば真(true),しなければ偽(false)を返す hs = {"A"=>3, "B"=>5, "C"=>2} # "A"~ "Z"の各文字列について,hsにキーとして存在する場合は # 対応するデータを表示 ('A'..'Z').each do |c| if hs.key?(c) then puts "key '#{c}' found, value: #{hs[c]}" else puts "key '#{c}' not found" end end
プログラム例 1行ずつ文字列を読み込み,各文字列が何回出現したかをカウントして表示 ちなみに chomp! は chomp の破壊的バージョン count = Hash.new(0) # デフォルト値を0とする空のハッシュ print "> " while line = gets do # 1行ずつ読み込み,lineに代入 line.chomp! count[line] += 1 # 文字列の出現回数カウントを1増やす print "> " end count.each do |k, v| # 各キー,データのペアに対して puts "#{k}: #{v} times" end
実行例 前のスライドのプログラムを実行
Tips: ハッシュ要素の表示 ハッシュを式展開した場合は,文字列に変換されて表示される なお,順序は不定なので各要素は入れ替わる可能性がある printとputsに引数としてハッシュを与えた場合の挙動 配列と違い,putsでも各要素がputsされる動作とはならない hs = {"A"=>3, "B"=>5} puts "#{hs}" # {"A"=>3, "B"=>5}と表示 hs = {"A"=>3, "B"=>5} print hs # {"A"=>3, "B"=>5}と表示 puts hs # {"A"=>3, "B"=>5}(改行) と表示
ハッシュを返すサブルーチン サブルーチンは,ハッシュを返すこともできる 同様に,ハッシュをサブルーチンの引数とすることもできる # 文字列中の各文字の出現回数を数える def count_char(s) count = Hash.new(0) # each_char は文字列中の文字を1つずつ取り出す s.each_char do |c| count[c] += 1 end count # countの値(ハッシュ)を戻り値とする end str = "This is it!" cnt = count_char(str) # ハッシュを戻り値として受け取る puts cnt
Tips: ハッシュのコピー 配列と同様,ハッシュの内容は別の変数に代入しただけではコピーされない 内容をコピーするには,dupメソッドを用いる a = {"X"=>1, "Y"=>7} b = a # aを bにコピーしたつもり a["X"] = 5 # aの要素を変更すると… print a # {"X"=>5, "Y"=>7} print b # {"X"=>5, "Y"=>7} (bも変更されている) a = {"X"=>1, "Y"=>7} b = a.dup # aと同内容のハッシュを新たに作成 a["X"] = 5 # aの要素を変更すると… print a # {"X"=>5, "Y"=>7} print b # {"X"=>1, "Y"=>7}(bは変更されない)
練習問題 5枚前のスライド (「プログラム例」) のプログラムを,キーとなっている文字列の辞書順に結果が表示されるように改造せよ。 入力として自然数を読み込み,その数を素因数分解した結果を表示するプログラムを作成せよ。 まず,入力された自然数の素因数をキーとし,その個数 (指数表記した際の指数) を値とするハッシュを作成せよ。 例えば与えられた自然数が720 (=24×32×5) であれば,キーが2で値が4の要素,キーが3で値が2の要素,キーが5で値が1の要素の計3要素を持つハッシュとなる。 作成したハッシュを用いて,素因数分解した結果の文字列を「p1**e1 * p2**e2 ...」 の形式で 「素因数の昇順となるように」 表示せよ。 720であれば 2**4 * 3**2 * 5と表示する。 参考として,「素因数分解の結果を文字列として返す」 サブルーチンを次のスライドに示す。 (そのまま使えるわけではない) 720を引数として与えると "2 * 2 * 2 * 2 * 3 * 3 * 5"という文字列を返す。
参考: 素因数分解を行うサブルーチン def prime_factorization(n) x = n y = 2 result = "" while y * y <= n and x != 1 do while x % y == 0 do break if x == y result += "#{y} * " x /= y end y += 1 end result += "#{x}" if x != 1 result end
課題 今回のスライドの練習問題4問をそれぞれ解くためのプログラムを作成せよ。 それぞれ別個のプログラムとして作成すること プログラム中に,コメントとして処理内容の説明を入れること 提出物 4つのプログラムファイルをまとめたZipファイル 提出期限 10月24日(金) 23:59 keio.jp 「授業支援」 上で提出