2024年1月1日月曜日

Rails 7にimportmapのみでBootstrap, jQuery / jQuery-UIを設定する方法

かねてよりRailsをしばしば利用しており,Railsアプリの作り方を教える授業も担当,だいぶ内容が古くなってしまったが書籍も出しているくらいにはRailsを愛用している.しかし,Rails 7になってフロント周りがいろいろと変更されて,ネットの情報がのきなみ信用ならない状況になっていて,どこかで最新の状況を整理しておかねばならないなと感じていた.

なにしろ,jQuery-UIなんかを使ってちょいと気の利いたことをやろうとしても,どうにもうまくいかず,困った状況なのである.そこで,今回,BootstrapとjQuery / jQuery-UI を importmap の仕組みで導入する方法を整理した.最終的に,次図のような状況を実現するところまで解説する.

なお,動作を確認したバージョンは,次のとおりである.

ruby: 3.2.2, rails: 7.0.8, bootstrap: 5.3.2, jQuery: 3.7.1, jQuery-ui: 1.12.1

また,次の記事を参考にした.

前者のBootstrapの導入方法は,一箇所,そのままだとエラーになる箇所があるうえ,若干,記述が曖昧な箇所がある.一方,後者は問題なく実行できた.やはり確実な情報を入手するには英語の文献にあたれ,ということだろうか.

アプリの構築

まず,テストのためのアプリの雛形を構築する.homeコントローラだけを作っておく.

$ rails new Test; cd Test

...

$ bin/rails g controller home index

config/routes.rb は次のようにしておこう.

Rails.application.routes.draw do

  root 'home#index'

end


Bootstrapの導入

Gemfile に次を追加する(bundle add bootstrap を実施でもよい).

# Bootstrap 5

gem "bootstrap", "~> 5.3.0"

また,以下の gem "sassc-rails" と書いてある行のコメントアウトを外す.

# Use Sass to process CSS

# gem "sassc-rails"

これを,次のようにする.

# Use Sass to process CSS

gem "sassc-rails"

Gemfileを書き換えたら,gemをインストールしよう.

$ bundle install

Rails 7.0.8 だとimportmapがインストールされているはずなので,config/importmap.rb には次の2行を追記する(bin/importmap pin bootstrapコマンドでも必要な情報が記入されるが,実行時に大量のエラーが出るという問題がある).

pin "bootstrap", to: "https://ga.jspm.io/npm:bootstrap@5.3.2/dist/js/bootstrap.esm.js"

pin "@popperjs/core", to: "https://ga.jspm.io/npm:@popperjs/core@2.11.8/lib/index.js"

app/javascript/application.js に下記を追記する.

import "./bootstrap"

スタイルシートをscssに変更する.

$ mv app/assets/stylesheets/application.{c,sc}ss 

app/assets/styleseets/application.scss には次のように記述する.デフォルトではコメントが記載されているだけなので,コメントの後に次のように追記すればよい.

@import "bootstrap";

確認のために app/views/layouts/application.html.erb の<body>〜</body>を次のように修正する.localhost:3000にアクセスしたときに,少しインデントされて表示されていれば,Bootstrapが有効になっているはずである.

...

  <body>

    <div class="container">

      <%= yield %>

    </div>

  </body>

</html>


Stimulus JSのセットアップ

さて,次はjQueryとjQuery-UIである.が,その前に,stimulus.js を設定しておく.まず,次のコマンドでstimulus.jsコントローラを作成する.

$ bin/rails g stimulus home

stimulus.jsを利用するために,app/views/home/index.html.erb を次のように記述する.

<div data-controller="home">

  <h1> This is home page</h1>

</div>

さらに,app/javascript/controllers/home_controller.js を次のように記述する.

import { Controller } from "@hotwired/stimulus"


// Connects to data-controller="home"

export default class extends Controller {

  connect() {

    console.log("home controller has been connected");

  }

}

こうしておくと,data-controller="home" が記載されたページが表示されたときにこのJavaScriptが有効になるらしい.localhost:3000にアクセスしてブラウザの開発コンソールをチェックすると,このメッセージが記録されていることを確認できるだろう,

jQueryおよびjQuery-UIの導入

いよいよjQueryとjQuery-UIを導入する.まず,Gemfileに次の記述を追記する.

# Use jquery as the JavaScript library

gem 'jquery-rails'


# Use jquery-ui for pretty UI

gem 'jquery-ui-rails'

忘れずにgemをインストールしておこう.

$ bundle install

app/assets/styleseets/application.scss には次の行を追記する.

@import "jquery-ui.css";

次に,app/javascript/jquery_ui.js というファイルを作り,次を記載する.

//= require jquery-ui

さらに,app/javascript/application.js に次を追記する.

import "jquery"

import "jquery_ujs"

import "./jquery_ui"

あと,もう少し.config/initializers/assets.rb に次の行を追加する.

Rails.application.config.assets.precompile += %w( jquery.min.js jquery_ujs.js )

さらに,config/importmap.rb には次の2行を追記する.

pin "jquery", to: "jquery.min.js", preload: true

pin "jquery_ujs", to: "jquery_ujs.js", preload: true

これで準備OKである.

動作確認

うまく設定できたかどうか,動作確認しよう.

app/views/home/index.html.erb は,次のように書き換える.

<div data-controller="home">

  <h1 class="mt-3"> This is home page</h1>

  <h6> Pick date using jQuery Datepicker </h6>

  <p>Date: <input type="text" id="datepicker"></p>

  <br>

  <hr>

  <h6> JQuery Draggable Element </h6>

  <div id="draggable" class="ui-widget-content">

    <p>Drag me around</p>

  </div>

  <br>

  <hr>

  <h6> Click Event using JQuery </h6>

  <button id="btn-click" class="btn btn-primary"> Click Me </button>

</div>

これらの要素をコントロールするための app/javascript/controllers/home_controller.js も,次のように記述しておこう.

import { Controller } from "@hotwired/stimulus"


// Connects to data-controller="home"

export default class extends Controller {

  connect() {

    console.log("home controller has been connected");

    $("#datepicker").datepicker();


    var initial_val = 0;

    $("#btn-click").click(function (e) {

      e.preventDefault();

      var date_value = $("#datepicker").val();

      alert(`button has been clicked ${initial_val} and date ${date_value} `);

      initial_val+= 1;

    });


    $(function() {

         $("#draggable").draggable();

      });

  }

}

サーバをいったん止め,再起動する.

$ bin/rails s

ブラウザでアクセスして,冒頭に示したような画面が現れれば,OKである.それぞれのアイテムが適切に動作している状況を確認されたい.

以上が,Bootstrap,jQueryおよびjQuery-UIを,Rails 7にNode.jsを使わないで導入する方法である.

おまけ

jQuery-UIに拘っている理由は,とあるプロジェクトでスライダー要素を使いたいからである.スライダーも使えることを,確認しておこう.app/views/home/index.html.erb に次の記述を追記する.どこでもよいが,とりあえずは,ボタンの下くらいに配置しておくことにする.

  <hr>

  <h6> JQuery Slidar Element </h6>

  <div id="slider" class="col-6 ui-widget"></div>

app/javascript/controllers/home_controller.js の修正は次のようにする.9ステップのスライダーを作り,スライダーが弄られたら値をコンソールに,都度,吐き出すというコードである.

...
    
$(
function() {

       $("#draggable").draggable();

       $("#slider").slider({ min: -4, max: 4, step: 1, value: 0,

           slide: function(event, ui) { console.log(ui.value); } });

    });

  }

}

動かして,試してみよう.うまくスライダーを使えるようになっただろうか.

実際には,コンソールに吐くのではなく,jQueryのattr()メソッドなどを使ってフォームのhidden_fieldにでも値をセットするようにすれば,スライダーの値をサーバに送り返すようなインタフェースを作るのは容易であろう.

2023年12月30日土曜日

シミュレーションゲームを使った行動分析

Twineというツールがある.もともとこのツール,「対話的非線形物語(interactive, nonlinear stories)」あるいは「あなた自身の冒険を作成(create-your-own-adventure)」と呼ばれる,オンライン版のテキストアドベンチャーゲームを作成するツールである.

このツールを用いて行動分析できないかと今年の卒研生が考えた.彼らは「避難行動のシミュレーションゲーム」を作成し,いま,その分析をしながら卒論を書いている.

もとより簡単な行動ログを記録する機能が用意されていたが,より簡便かつ確実に,詳細な行動記録を取れるように拡張してみた.本稿ではその概要を説明する.

Snowmanの利用

まず,Twine上で動作させるstory formatとしてSnowmanと呼ばれるものを利用する.Twineでは,いくつかのスクリプティングシステムを切り替えて活用できる.そのスクリプト言語のことをstory formatと呼んでいる.

デフォルトではHarloweと呼ばれるものが組み込まれているが,これを,Snowmanと呼ばれるstory formatに切り替える.Snowmanは,CSSやJavaScriptをある程度理解している人向けに用意されているもので,PHPやERB(Embedded Ruby)のように,<% …… %>や<%= …… %>といったタグを用いてJavaScriptをそのまま埋め込められる.

なお,Snowmanへの切り替えは,Twine編集画面のStoryタブにあるDefaultsメニューから行う.

Story JavaScriptの設定

Story formatをSnowmanに切り替えたら,まず,StoryタブのJavaScriptメニューを開き,Story JavaScriptに以下のコードを記載する.

window.story.state.hash = {};


// create a unique ID

window.story.state.uid = Date.now().toString(16) +

            Math.floor(1000 * Math.random()).toString(16);


$(window).on('sm.passage.shown', 

             function (eventObject, passageObject) {

  window.story.state.

    hash[passageObject.passage.name] = eventObject.timeStamp


  // disable the browser's back button

  history.pushState(null, null, null);

  return;

});


// to show the alert to quit the game

$(window).on('beforeunload', function (event) {

  event.preventDefault();

  return;

});

このコードは,ストーリーが起動されたときに最初に走るJavaScriptのプログラムである.なお,Snowmanでは,JavaScriptの動作スコープは各passage(ページのこと)内に閉じられており,グローバル変数をwindow.story以外に持たない.したがって,ストーリー全体でデータをやりとりしたい場合には,基本的にはwindow.story.state変数にいろいろと付け加えていくしかない.

そのような理由により,まずは,window.story.state.hashという連想配列を用意し,動作状況はそこに記録していくことにする.

なお,もうひとつ,window.story.state.uidというユーザIDを記録する変数も用意し,そこに一意な文字列として作成するIDを格納する.

Snowmanでは,passage(ページ)遷移時にsm.passage.shownというイベントを発火する.各passageのアクセス記録を取りたければ,そのイベントを受けて,先の連想配列に,passage名をキー,その時点でのタイムスタンプ(Unixエポック)をバリューとして,記録するようにしておけばよい.passage名はpassageObject.passage.nameとして,タイムスタンプはeventObject.timestampとして与えられるので,それを連想配列に記録する.

残りのコードは,ブラウザの「戻る」ボタンを無効にしたり,途中でやめようとしたときにアラートを発したりという設定を行うものである.

記録のテスト

簡単なストーリーを用意した.次の図は,四つのpassageから成るストーリーである.Story JavaScriptには先のコードが入れられており,最後のページには,記録されたユーザIDとJSON形式で提示されるアクセス一覧を表示するコードが埋め込まれている.

このストーリーを実行して最後のページ(The last page)まで遷移すると,次の画面が現れる.

一意なユーザIDと,JSON形式で表現されたアクセスログが表示されていることを確認できる.

ところで,ここまで注意深く読んできたひとは,"The last page"が記録されていないじゃないか?ということに気付いただろう.そのとおり.残念ながら最後のページの記録が残らない.これは,どうも,sm.passage.shownというイベントが,ページのレンダリングの「後で」発火するから,という仕様によるものらしい.

なお,ドキュメントにはsm.passage.hideやsm.passage.showというイベントもあると書いてあり,shownではなくshowを使えば最後のページも記録できそうなものだが,現時点で利用できるTwine 2.8.0とSnowman 2.0.2の組合せではsm.passage.shownしか使えなかった.残念だが,致し方ないところである.最後まで記録するためには,最後にもうひとつ,ダミーのページを用意して対処する必要がある.この点には注意しなければならない.

サーバへ送信

さて,ここまで確認できれば,あとは,最後のpassageにサーバへ記録を送信するコードを埋め込み,サーバ側ではその送信を受け取るコードを用意すればよいだけである.

<%

  $.ajax({

    url: 'https://your.server.domain/php/tlog.php',

    type: 'POST',

    data: { 'uid': window.story.state.uid,

            'log': JSON.stringify(window.story.state.hash) },

    dataType: 'json'

  });

%>

このコードはyour.server.domainというサーバに結果を送信するAJAXのコードである.ユーザIDとJSON形式で表現したアクセスログを送っている.

なお,アクセスログのデータは ... 'log': JSON.stringify(window.story.state.hash) }ではなく... 'log': window.story.state.hash }としてもそのままJSONデータとして送信できるが,サーバ側での処理を簡易化するため,この段階で文字列データ化して送信している点に注意.

サーバ側の処理

サーバ側のプログラムは,/var/www/html/php/tlog.phpというファイルにおいて,次のように記述する(サーバ側はUbuntu 20.04, PHP 7.4.3 および SQLite3 3.31.1を使用).

<?php

  $uid = $_POST['uid'];

  $log = $_POST['log'];


  $dbfile = './tlog_data.sqlite3';

  $conn = new PDO('sqlite:' . $dbfile);


  // create table

  $ddl = 'CREATE TABLE IF NOT EXISTS userdata(

             id integer primary key autoincrement, 

             uid string, log text)';

  $conn->query($ddl);


  // insert posted data

  $sql = 'INSERT INTO userdata(uid, log) 

          VALUES (:uid, :log)';

  $stmt = $conn->prepare($sql);

  $stmt->execute(array(':uid' => $uid, ':log' => $log));

?>

以上の比較的単純なコードで,ユーザ行動を記録できる.次のスクリーンショットは,記録の例である.左からID,ユーザID,アクセスログが記録されている状況を確認できよう.

今回は単純な線形のストーリーであるため,全て同じ行動でタイムスタンプが違うだけだが,複雑な非線形ストーリーを用意すれば,全ての選択行動をデータベースに記録でき,その結果を分析すればユーザの判断行動もわかるという仕組みである.

行動分析の例

最後に,行動分析の事例を紹介しておこう.いま卒論に取り組んでいる学生たちが用意したストーリーで得られた,ユーザの選択行動データである.テーマは「避難行動」,大地震に遭遇したときの避難状況において,ユーザはどういった判断をするのかという分析である.

次の図は,アクセスログから解析した行動状況を示すものである.ノードは各passageを示し,ノードに付記されている数値は,そのpassageを通過したユーザの数を表す.また,ノードの色は滞在時間の平均値から計算されたものであり,寒色系は短い時間,暖色系は長い時間を表している.白は誰も通過しなかったノード,および,最後のノードである(最後のノードはそもそも滞在時間を計測できないため).

この図は,アクセスログと,Twineから出力される.tweeファイルに基づき,PythonとGraphvizを用いて作成した.アクセスログだけでなく.tweeファイルも必要な理由は,誰も通過しなかったノードはアクセスログから掘り起こせないからである(なお,その作業を行ったことにより,どこからも参照され得ない不適切なノードの存在も発覚した.そのような不適切なノードは分析作業の対象外として,先の図には表していない).

この結果から,いくつか意義のある分析結果が得られている.ここでは,ひとつの興味深い事例を紹介しよう.


「道が分かれている」というpassageでは,ハザードマップを見たことがあるか否かの質問を投げかけている.「はい」を選んだグループと「いいえ」を選んだグループでは,明らかに,その後に投げかけられた「どの場所を通っていきますか?」という設問の回答に差が出ている.

見ただけで,ハザードマップを見たことがあるグループは大通りを選ぶ傾向がある一方で,見たことがないグループはそうでもないことがわかる.実は,ハザードマップには大通りを選ぶべきであるヒントが記されていた.したがって,素直に解釈すればハザードマップを見たグループは大通りを選ぶ一択になるはずなのである.この結果に対してカイ二乗検定を実施したところ,1%水準で有意差が示された.

2023年12月9日土曜日

悪質タクシーにご用心

ゆうべ,こんなことがあった.

昨日はサイバーワールド研究会が三田のNECで開催されたため,夜はNEC芝倶楽部で懇親会をした.懇親会を早い時間から始めたので,お開きになったのは20時頃.そして昨夜は,たまたまMRIの元同僚の皆さんが赤坂で飲んでいるとかでそちらからも声がかかっており,三田から赤坂に移動することにした.

三田で飲んでいたOさんと二人で赤坂に向かうことになったが,すでにかなり酔っ払っていて田町から赤坂までの最適な移動方法がすぐに思い浮かばない.大柄なOさんということもあって「面倒だからタクシーで行っちゃおう」と,OさんがタクシーGOというアプリで車を呼んだ(※ここ重要なので覚えといてね).

しばらくして個人タクシーが一台やってきて,我々はそれに乗車,赤坂に向かった.なお,座席は私が奥で運転手の後ろ,Oさんは入り口側である.タクシーに乗るのはずいぶんと久しぶりで,(初乗り500円だけど,100円ずつ,けっこういい調子でメーターが上がっているなあ)などと酔った頭でボーッと見ていた.

よく喋る運転手で,Oさんと運転手はずいぶんいろいろ話をしていた.タクシーやるなら個人タクシー,リスクもあるけど儲かる,なんて話.私はなんとなく億劫になっていて,それを聞くともなく,外を見たりメーターを見たりと所在無げにしていた.

さて,赤坂に近づき「どのへんでお降りになります?」と尋ねられたので「TBSのあたりで」と答えた.赤坂の手前,溜池山王を過ぎて日枝神社の前を赤坂に曲がる交差点でちょうど赤になったので,「あ,いまちょうど赤信号だから」と,我々はそこで降りることにした.

メーターを見たら2,400円.そんなもんかなと,財布から1,000円札を2枚出す.ポッケのなかの小銭を探していたら「あ,2,000円でいいですよ」と運転手.「えらいまあ気前のいい運転手だなあ,メーターがやたらと早く回るタクシーだから,それ差っ引いてそういうサービスなのかなあ」などと酔った頭で考えつつ,千円札2枚,2,000円を支払って車を降りた.

さて,先に降りていたOさんと歩道で歩きながら,その件に付いて話し出すと……

「え?お金払っちゃったの?タクシーGOで呼んでるから,2,500円,俺のクレジットカードから支払われるんだよ?」と,アプリの画面に大きく2,500円の文字が出ているのを見せるOさん.

おいおい,さっきの2,000円,いったい何だったんだ?

幸にして,ちょうど信号が変わった直後のタイミングだったので,左に曲がろうとしていたタクシーに追いついた.窓を叩いたら,運転手が窓を開けて「これでしょ」と2,000円を返してきた.

_人人人人人人人人人人人人人人人人_
> 「これでしょ」じゃねーだろ! <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^ ̄

わかってて,しれっとカネ受け取りやがったな.

アプリで呼んだので,素性はバレている.車のなかで「タクシーセンターに苦情を3回入れられると営業できなくなる」なんて話をしていた.さすがに苦情入れられるのが怖くなったかな?

タクシーGOの仕組みを知らなかった私もうっかりしていたが,忘年会,新年会で酔っ払ってまともな判断力を失いそうになるこの季節,皆さんも.お気をつけあれ.



2023年12月2日土曜日

名前なんて飾りです偉いひとには(以下略

「飯尾」という名前,とくにありふれているわけでもなく,かといってさほど珍しいわけでもなく,なんとなく中途半端な位置付けにある.全国には1万人弱くらいの飯尾さんが居て,その順位は1,600番目くらいだそうだ.最近ではお笑い芸人のコンビ「ずん」の飯尾がTVで多少は活躍しているようで,それなりに認知度が上がってきたか?という感じ.

1994年,三菱総研に入社したときに,1,000人に満たない規模の会社にもかかわらず別の飯尾さんがいらっしゃり,社員名簿に「飯尾(淳)」とカッコ書きで下の名前が付記された.学生時代には経験したことがなかった体験だったので,そのときは少し興奮した.なお,その後,同様の経験はなく,しばらくして先輩の飯尾さんは定年退社したため,カッコ書きは取れてしまった.今ではそもそも,社員名簿そのものが作られないご時世だ.

そんな飯尾という名前に多少の愛着はないこともないが,困るのが海外に行くとしばしば Lio にされてしまうことである.子音が一切なく母音だけ3文字という名前は奇異にみえるらしく,最初のI(アイ)を小文字のl(エル)だと勝手に判断された結果,Lioにされてしまう.

その件で初めて困ったのは,2007年にドイツに出張したときのこと.日本からの飛行機は,夕方,フランクフルトに着陸し,私は空港から電車を乗り継いで,その郊外にあるダルムシュタットという街に赴いた.翌日の予定として,その街にあるフラウンフォーファー研究所への訪問が目的だったが,とにかく,ダルムシュタットの街中にある予約してあったホテルに着いたのは,もう日も落ちて20時を超えた時間だった.

小雨模様でうら寂しい状況であり,かつ,季節は初冬でたいへん寒い.駅からタクシーでホテルの前まで送ってもらい,ホテルを見つけたときには,とにかく落ち着けそうだと安堵した.しかし,その宿はずいぶんと小さな宿で,夜になっていたので通りに面した入り口はすでにシャッターが降りていた.せっかく到着したのに入れないじゃないか.

とにかく部屋に入りたいとあたりを見回してみると,傍に2階へ上がる階段があり,その入り口にキーのディスペンサーが設置してあった.インストラクションを読むと「If you have a reservation, enter your surname as the password to deliver the key for your room.(予約がある方は,姓を部屋の鍵のパスワードとして入力してください)」とある.

そこで何度も I, I, O と入力してみたのだが,キーは出てこない.このときはまだ Lio に間違えられた経験も少なく,まさか Lio として登録されているなどとは思いもよらなかった.

予約がなくとも,部屋が空いていればクレジットカードで部屋を確保できるよとの指示があり,クレジットカードで部屋を押さえることができた.ひと安心して部屋に入ると,部屋の電話がすぐに鳴り「もうひと部屋必要になったのか?」と宿のマスターが尋ねてきた.

そうではないと断り,その夜はそれで一件落着となった.Lio で登録されていたからキーが出てこなかったのだと知ったのは翌朝,食堂でマスターと話をして正確な状況を確認できたときである.

このときの出張はドイツ,イタリア,フランス,イギリスと1週間で4国を回る弾丸出張で,結局のところ,泊まった4箇所のホテルのうち3箇所で Lio と間違えられていたことが判明した.なんてこったい.ちなみに,最後に訪問したイギリスでは,いつ帰る?と入国審査官に尋ねられ,「tomorrow(明日)」と答えたら,「ワーカホリックの日本人よ,もっとグレートブリテン訪問を楽しみなさい,次に来るときには絶対に1週間以上滞在すること!」と叱られた.余計なお世話じゃ.

なお,JALやらANAやら,日系の航空会社に乗ると「イトー」様と呼ばれることが頻繁にある.彼ら彼女らは Iio ではなく Ito に見慣れているからだ.「伊藤様,いつもご利用ありがとうございます」と何度言われたことか.こちらもすでに慣れっこであり,心はすでに伊藤淳である.なお,大学時代に同じ学科で学んだ同級生に伊藤淳史という友人がいる.彼の名は「いとうあつし」.同姓同名である俳優の伊藤淳史は我が家の近くに住んでいたはずで,彼のご子息がうちの息子と同級生だか一つ下だったか,というのは完全に余計な話.

IT業界には(もうすでにお亡くなりになっているが)itojun氏という有名な方がいて,Googleなど,iiojun でエゴサーチすると「もしかして itojun」などと言ってくる.全く,失礼千万である.名前なんて飾りだとはわかっていても,これはちょいとばかり酷くない?

さて,だいぶ話が脱線してしまったので,元の話に戻そう.今回参加した国際会議もネームプレートには Lio と記されていた.こちらも逆手にとって「外国の皆さんからするとヘンな名前だってことは知ってるから,驚かない.Junって呼んでね」と自己紹介のネタにする.全国の飯尾さんにおかれましては,国際的な自己紹介に使えるネタですよ?

最後に,「脚なんて飾りです,偉い人には……」という某アニメの有名なセリフを借りて,名前なんて飾りにすぎないとあらためて主張したい.やれハシゴ高だツチ吉だとこだわる人をたまに見かけるが,中身で勝負せいと言うのはお節介か.

ところで,うちに「昔の漢字コードだと対応する字がないのではてなマークにされちゃうんです.だからカタカナ表記にしています」という学生が一人いる.それはさすがに可哀想すぎる.聞けば,成績証明書の発行システムだかなんだかが対応していないらしい.Unicodeだと対応できているようなので,古いシステムを使っている組織は早いとこ新しいシステムにリプレイスしてあげてほしい.

2023年11月29日水曜日

2023冬季HCD研究発表会振り返り

2023年度冬季HCD研究発表会が11月25日,鹿児島女子短期大学にて開催された.かなり昔に札幌でHCD研究発表会が開催されたことがあるとは聞くものの,少なくとも私が関与するようになってからは,初めての地方開催である.今回の開催にあたり,ローカルのマネジメントに尽力くださった近藤先生とサポートくださった皆様方には感謝を申し上げたい.

今回のHCD研究発表会は,鹿児島での地方開催というだけでなく,東京の芝浦工科大学豊洲キャンパスをサテライト会場とした二会場開催という点でもきわめて挑戦的な開催であった.コロナ禍以降,現地参加とオンライン参加を許容するハイブリッド開催で実施していた本研究発表会ではあるが,今回は地方開催ということもあり,東京は豊洲にサテライト会場を設置して二元中継を行う新しい試みに挑戦した.

しかも,サテライト会場からの口頭発表だけでなく,ポスターセッションを鹿児島会場と豊洲会場の二ヶ所で行うという,あまり聞いたことのない斬新な試みにも挑んだ.ポスター発表を2会場で行う点に関しては,HCD研究発表会名物?の「ポスター発表中継隊」が大活躍してくれた.これもICT進化の恩恵であろう.

さらには「実践発表」カテゴリを設けたことにより,発表数が例年になく増えた(口頭発表とポスター発表を合わせて28件というのは例年の1.5倍相当である).実践発表の是非については本稿では省略するが,気軽に発表できる機会を提供できた点は,自画自賛ながら,評価に値するのではないかと考える.そのため,口頭発表の件数も20件となり,1日に実施するにはギリギリのスケジュールとなった.

シングルトラックで実施するにはほぼ限界の発表件数のため,今後は,マルチトラックにして対応せざるを得ないかもしれない.自作の研究会マネジメントシステムには,その含みを持たせてある.研究会規模の拡大がいよいよ現実味を帯びてきた.今回の開催がHCD研究発表会をさらに発展させるための試金石となっている状況をひしひしと感じる.これからの発展が楽しみである.

なお,HCD研究発表会のウリの一つに,発表に対するコメントを発表者にフィードバックするという特典がある.研究会に参加しているHCD-Netの理事と評議委員には,発表の評価と(できるだけ前向きな)コメントの記載をお願いしている.

そして,これも自画自賛になるものの,自作のシステムにより発表の評価とコメントをリアルタイムで集計するしくみが出来上がっている.集計されたコメントは,翌日にメールで発表者にフィードバックされる(この作業はいまのところ私が手作業で実施しているが,これもシステム化して自動でメールを送れるようにしたいところである).これは参加者におおむね好評で,これも発表者を増やすための一つのアイデアとして効果があるだろう.

いずれにしても,今後のHCD研究発表会のあり方として,春季冬季のいずれかは東京開催,そして他方は地方開催というルーチンを確立できればと画策している.そのためには,地方の拠点を確固たるものとせねばならない.

2023年11月23日木曜日

オンライン学習状況を可視化すると何が分かるか

IT系の某オンライン学習サイトの模擬試験受験状況データをいただいて,その分析を進めている.なかなか興味深い結果がみえてきているので,その一部を紹介したい.なお,このオンライン学習サイトは,無料で模擬試験を受けられるもの.その模擬試験は40問ひとセットで,40問の問題はランダムに出題される.

まず,どれだけのユーザがどのくらい模擬試験にチャレンジしているか?というデータを集計してみた.1回の試験にIDが振られているので,それらの数をユーザごとに集計する.その結果,最大で593回も受験しているユーザがいた(なお,次点が403回,その次が121回で,それ以下は100回以下である).

ほとんどが数回から十数回のチャレンジであるため,25回以下に限定してヒストグラムを描いてみた.その結果が次の図である.

1回しか挑戦していないユーザが多いのは予想通りで,指数関数的な分布を描くであろうという状況もほぼ予想したとおりである.特異的な値がみられず,比較的きれいなカーブを描いているのは,回数に注意するユーザはほぼいないことを示唆していると考えられる.

ところで,40個の問題が出される模擬試験だが,途中で諦めてしまうユーザも多いはずだと考えた.そこで,何問まで解答したか?について集計してみた.

ところがこれが意外なことに,ほとんどのユーザが40問,最後まで解答している.皆さん真面目だなあ.

しかし,それでも途中で諦めてしまうユーザも少なからずいる.そこで,何問まで解答して,そのあと諦めてしまったのかを,39問の解答までに限って集計し,ヒストグラムを描いてみた.次の図をみていただきたい.

こちらも,ところどころ特異点があることと,35問以降を除くと,指数関数で近似できるかできないか微妙な感じではあるが緩やかに減少するカーブを描いている.

しかし,先のグラフと異なるのは,5, 10, 20といった,明らかに特異点と考えられる点が観察できることである.これは,5問やってもういいや,10問やったからやめよう,20問まで頑張った……というように,キリの良い数でリタイアしている状況を表している.

35問から徐々に増えていることについては次のように考えられよう.すなわち,そこで諦めたというよりは,それ以降,答えがわからないのでスキップした,という状況が一定数あるとの示唆である.とくに39問が極端に多くなっている理由は,39問は解答したが40問目がわからなかった,という状況であろう.

(追記)学生から,タイムアップの影響じゃないですか?との指摘を受けた.たしかに,制限時間があるため,最後のほうは制限時間以内に解けなかったためにスキップされていると考えるほうが自然かもしれない.

2023年11月18日土曜日

すっぽんパワー

西麻布から六本木にかけて散歩した.たまの散歩はいろいろと面白いものを発見するので楽しい.

いきなりのスッポンスープ,高額自販機,ひと缶1,000円なりィの缶スープである.(缶飲料としては)破格のお値段.

遠目にみたら,自販機の横に「日々是好日 だしのある風景」とあった.

(ああ,最近よく見るだしの自動販売機だなあ)と思いつつ近づいていくと,まさかのスッポンスープである.スッポンスープにもノーマルとプレミアム版,2種類あるのが面白い.廉価版はひと缶760円と,プレミアム版に比べるとリーズナブル.いうて滅多に見られない価格設定ではあるが.

このスッポンスープ缶,いったいどんな人が買うんだろう?

場所柄,「これからいっちょ決めてやんべ!」とハッスルハッスルなオッサンが,1,000円札をズバッと投入,ガコンッと出てきたスッポン缶を腰に手を当ててグイッと飲み干すとか.そんでもって「オレ様の波動砲,エネルギー充填120パーセントッ!」なんて下品な冗談飛ばしながらガハハと笑いつつ,夜の六本木に消えていく…… なんて光景が目に浮かんだが,さすがに昭和な風景すぎますか?

2023年11月10日金曜日

横浜ぶらり旅

横浜にオフィスを構える某社を訪れて打合せする約束があった.午後イチの訪問予定だったので,少し早めに行って中華街で腹ごしらえを目論んだ.目指すは横浜中華街,香港路は安記のお粥である.

久しく訪れていなかった中華街,前回はいつ来たろうか.何年も前なことは間違いがない.みなとみらい線の元町中華街駅から歩き,東門から入る.道路がきれいになっていて,ずいぶんと垢抜けた印象を受けた.昼食にはいささか早い時間帯だったものの,焼き小籠包や大ぶりの肉まんなど,今でもあちこちで行列ができている.

香港路……,香港路……,中華街のメインストリートを西に向かって歩いていけば,左側に隠れるような入り口があるはず.少し歩くと……,見ぃつけた.

お粥の名店,安記は香港路に入ってすぐのところにある.なお,この路地には他にも海員閣という老舗の名店があるが,最近はお店の事情で営業をだいぶ縮小しているらしい.営業しているうちに,また行っておいたほうがよいかも.

さて,今日は安記である.もうすっかりお粥の口になっている.扉を開けて,店に入るなり,おばちゃんが「今の時間はお粥だけだけど」と告げてくる.ばっち来いやー!こちらはそのつもりで来ているのだよ.

メニューを見ずに,一言,「とりのお粥」と注文.880円.昔はもっと安かったような気がするが,このご時世だもの,仕方ないか.

待つことしばし,あっつあっつのお粥が運ばれてきた.これこれ.シンプルながら,抜群に旨い中華粥である.

レンゲで掬いながら,ふうふうして食べる.美味しいからといって,慌てて口に入れてはいけない.ヤケドする.

もう,一口一口が幸せを感じる旨さ.食べ進むと下のほうから鶏肉片たちがひょっこり顔を出す.これも旨い.トッピングの油条スライスもいい感じにふやけて旨い.途中で薬味を投入,それも旨い.気付けばあっという間に食べ終わっていた.

修学旅行のシーズンなのか,中華街は中高生で溢れていた.あいにくの雨模様だったが,久しぶりの中華街,安記のお粥で大満足である.

2023年11月7日火曜日

貴重な経験をした

エレベータの中に閉じ込められるという経験をした.人生で初めての経験である.ちょっとドキドキした.

それは,お昼休みに会議が予定されていて,早めの昼食に出た帰りのこと.自室に戻ろうと,エレベータに乗り13Fのボタンを押した.カゴに乗っていたのは自分ひとり.いつもどおり順調に階の表示が上がっていき,13になったところで,ガコンッと大きく揺れて停止した.

(ん?地震?)

最初に考えたのは,地震が来たかな?というものである.地震を検知して止まったのかな,と考えた.しかし,揺れは来ない.地震ではないらしい.

非常停止を検知したらしいエレベータから,「非常通話ボタンを押して,オペレータとお話ください」と音声案内が流れた.非常ボタンを押すなんて,これも初めての経験である.

ボタンを押す.すると,かなり大きな音でプーとブザー音が鳴り響く……ちょっと躊躇するレベル.まあでも,どうせ自分ひとりしかいないし,押し続けないとなあ.

頑張ってずっと押し続けていると,オペレータ氏と話が通じた.

オペレータ(以下「オ」):「聞こえますかー」「聞こえますかー」

私:「聞こえてますけど,こっちの声,聞こえますかー?」

向こうの声は聞こえるが,こちらの声が届きづらいらしい.非常ボタンをプッププップと押してみたり,あれやこれや試してみたりしてなんとか意思疎通を試みたところ,大きな声で喋れば会話も通じることが分かった.

オ:「いまどうなってますかー」

私:「13Fに向かってたんですけど,13Fになったところで,ガコンッって大きな音がして,止まっちゃいました」

オ:「開くボタン押してみてください」

私:「押しましたー.開きませーん」

オ:「そちら,どういう状況ですかー」

私:「わたしひとりです」

オ:「現場に連絡しますので,落ち着いて,お待ちくださいー」

落ち着いてと言われても,(このまま落っこちたりしないだろうな?)って,やっぱり心配になる.けっこう大きな音で,ガッコンッっていってたし.

そして,やはり気になったのはこの後の会議のことだ.ワーカホリックな私.幸にして電波が届くようだったので,事務室に電話する.「あ,飯尾ですけど……,エレベータに閉じ込められちゃいまして」間抜けなメッセージに,電話の向こうもびっくりしたのではなかろうか.

ひと通り経緯と状況を電話で説明し,はてさてどうしたもんかなと考えあぐねていたら,再びインターホンからオペレータ氏の声が.

オ:「いまから15分くらいで係員が向かいますのでー」

え?15分も待たされるの?さっきからもう10分は経ってるよ?

しばらくしたら,外から声がする.「おーい」ダメもとで声をかけてみると「大丈夫ですかー?」と呑気な呼びかけが扉越しに響いてきた.

「閉じ込められちゃいましたー」

こちらも負けずに間抜けな声で応答する.「もう少し待っててくださいねー」「待ちまーす」みたいな牧歌的なやり取り.しかし客観的にみたら,閉じ込められてる私,これってけっこうピンチでは?

閉じ込められてから20分くらい経っただろうか.「いま開けますね!」と,ようやく明るい希望の声が聞こえた.そして開けられるエレベータの扉から,パァアアア…… と広がる明るい光.いや,冗談じゃなくて.助けに来てくださった作業員の方には,たしかに後光が差していた.

次の写真を見ていただきたい.扉が開いたときの状況である.惜しい.あと1メートル.

助けに来てくれた方が「よじ登れますか?」と私に問う.何を仰いますやら,よじ登ってみますとも.まあ,1メートルくらいの段差,造作もない.よじ登ったときの衝撃でガコンとカゴが落ちて,胴体から真っ二つ……なんてことになったらヤダなあ,みたいな縁起でもないことは,考えないようにして,とにかく脱出成功.助かった.

以上が本日の顛末である.振り返ってみれば貴重な経験をしたといえるが,も一度体験したい?と問われればそれはもちろんNoだ.

2023年11月4日土曜日

ハーブの想い出

ハーブが好きだ.東南アジア諸国,とくにベトナムで食べられる,あの得体の知れない謎の薬草に魅せられている.しかし,日本で生活しているとあの謎の草を食べる機会にほとんど恵まれないのが残念でならない.

タイの中南部,ナコーンシンタンマラートという地方都市に出張した.いろいろ案内してくれた現地の担当者が「ここまで来てカノムジーンを食べないでは帰させない」と,カノムジーンの名店に連れてきてくれた.

カノムジーンとは,米を原料とする素麺のような麺に,ピクルスや豆などの付け合わせや鶏の揚げたものを添えて食べる料理である.単なる素麺ではなく,その上から何種類ものカレーソースから好みのものを選んでかける.辛いものからマイルドなものまでたくさんの種類を選べる.シーフードあり,チキンあり,どれも美味しい.お好みでミックスしても構わない.さらに,写真のようなたくさんの種類の葉っぱを好きなだけちぎり,最後に全部を混ぜて食べる.

雰囲気としてはベトナムのブンボーフエに似ている.ただし,こちらのほうがバラエティに富んでいて楽しい.

食べ方としてはこんな感じ.ただしこれは混ぜ混ぜする前の写真である.

ドライハーブとフレッシュハーブ

以前,ASEANの仕事に関わったことがあり,その際に,タイとベトナムから来ていた方々と話をした.曰く,「タイ料理はドライハーブを使い,ベトナム料理はフレッシュハーブを使うのだ」という.私はタイ料理もベトナム料理もどちらも好きだが,どちらがより好きか?と問われれば,草(ハーブ)だいすきという意味ではベトナム料理が優っているし,料理のバラエティからすればタイ料理も捨てがたいと感じていた.

さらに,タイの東北部,イサーン地方の料理に好きなものが多く,フレッシュなハーブを多用するラープはタイ料理のなかでも大好きなもののひとつである.イサーン地方はラオスを挟んでベトナムにも近い.さもありなん……と,これまでは思っていた.

ところが今回のカノムジーンである.ベトナムからは遠く離れたタイ中南部.ここでもこんなにフレッシュなハーブ使うんじゃん?

葉っぱはどこから?

ところでこの草たち,いったいどこから持ってきているのだろう,という疑惑がむくむくと湧き上がる.

今回連れて行ってもらったカノムジーンの店は郊外にあり,ど田舎の一軒家という風情の店だった.食卓はテラス席,といえば聞こえはよいが,庭に無造作に建てられた日除けの下,アウトドア席である.天気もよいので,これはこれで,なかなか気持ちのよい食事ができる.

さて,草である.

裏手に大きな鉢がいくつか転がっていたのだが,その鉢でいろいろな植物を育てている様子が窺えた.どうも,そこでこれらのハーブを栽培しているらしい.そこから収穫して持ってきているようにみえる.まあ,それはそれで全く構わないし,美味しいのだから問題ない.

夏に学生たちをベトナムに連れて行ったときに,この草の由来が問題になった.連れて行った学生のひとりが,「そもそもどこから持ってきた草なのか怪しすぎて食べたくありません」と言うのだ.

ホーチミン市の近代的な食堂である.タイの片田舎(といっては失礼だが)の郊外にある大自然に囲まれた店とは違う.それらの草はマーケットで買ってきた,「ちゃんとした」ものだろう.当該学生と一緒に市内のスーパーマーケットに行く機会があり,野菜売り場を覗いてみた.そこには多様なハーブがたくさん並んでいた.

「どう?これで大丈夫と思った?」と聞いてみたが,やはり抵抗があるとか.まあ,人それぞれか.

薬草だから大丈夫

話はいまから20年前,2003年に遡る.私が東南アジア関係の仕事に関わりはじめたころで,初めて東南アジアに出張したときのことだ.行き先はベトナム,よくある話で「水には気をつけろ.不衛生な水で洗っているから,生野菜は食べないほうがいい」などと脅かされていた.

いまでは「そんなことなかろう」と笑って済ませる話だが,なにしろ右も左も分からなかった当時のこと,忠実に言いつけを守っていた.しかし,1週間ほど滞在した最終日に,案の定,腹を下してしまう.

そのときに,現地駐在の長かった某社現地法人の支社長から「ハーブを食べなさい.これは薬草だから大丈夫」と,諭されたことを今でもはっきりと覚えている.

おそらく冗談半分だったろうが,強く印象に残っている.その後,生野菜なんかよりもっと危ない食べ物を口にして腹を思いっきり下したこともあり,いろいろな体験をした結果,私も成長した.東南アジア各国のどこに行っても生野菜は平気で食べるし,ビールも氷を入れて飲む東南アジアのスタイルじゃないと飲んだ気がしない.