投稿者: koji

  • ビジネス文書で重要な3つの要件

    まえがき

    『書き手が伝えたいことを読み手が正確に、速やかに読み取れるように表現するための要件は3つある。』

    リスト

    あとがき

    まえがきを含めて、照屋 華子『ロジカル・ライティング (BEST SOLUTION―LOGICAL COMMUNICATION SKILL TRAINING)』(東洋経済新報社、2006年)より。

    具体性と簡潔さは両立しがたい要件ですが、おそらくこの順序に意味があるでしょうから、具体性>簡潔さというメッセージかと。

    • 映画の登場人物への感情的参加の3段階

      まえがき

      『ケント大学の映画学教授であるマーレー・スミスは、映画の登場人物への感情的参加の三段階を定義している。』

      リスト

      あとがき

      まえがきを含めて、ポール・ジョセフ・ガリーノ、コニー・シアーズ 『脚本の科学 認知と知覚のプロセスから理解する映画と脚本のしくみ』(フィルムアート社、2021年)より。参考文献(1) からの引用とのこと。

      「感情的参加」という言葉が認知的共感の映画鑑賞版という感じで印象的でした。

      観客はメインキャラクターに同一化しているのではなく、感情移入しているのでさえない。観客が映画で見るキャラクターたちは危険に陥ったり苦しんだりするかもしれないが、観客自身はそうでないことを観客は知っている。しかし、観客はキャラクターが感じていることを十分に理解することができる。

        参考文献

        (1) Carlsten, Jennie. “Black Holes and White Space: Ellipsis and Pause in Steve McQueen’s Hunger.” Projections 9.1 (2015): 43-65.

      • 一緒に付けられることが多いタグ(共起タグ)を可視化する

        リスト投稿には1~5つのタグを付けるようにしています。同じタグが付いたリストは当然ながら性質が似ていて、類義語のようなタグが付くことが多くなります。

        たとえば「選択」というタグが付けられたリスト投稿には「決断」というタグも付けられがち。このような関係にあるタグを「共起タグ」と命名しておきます。

        共起タグを可視化したく、タグページの説明(description)欄に「このタグと共に使われていることが多いタグ」を付けてみました。

        フィルターフックの箇所

        TwentyTwenty テーマでは、タグページの説明は index.php から get_the_archive_description() を呼び出して表示しています。ですので、この関数内に用意されている ‘get_the_archive_description’ フィルターフックが使えます。

        add_filter( 'get_the_archive_description', 'lf_tags_description', 10, 1 );
        function lf_tags_description( $description ) {
        
            if( ! is_tag() ) {
                return $description;
            }
        
            /* 処理 */
        }

        上記の「処理」部分ではSQL文を使う場合と関数を使う場合の2通りのやり方を考えました。

        SQL文を使う場合

        「公開された」「投稿タイプがpostで」「listカテゴリーに属し」「表示しようとしているタグが付けられた」投稿群に付けられている他のタグを洗い出し、登場回数の多い順に5つを取り出すSQL文を書きました。

        たとえば「選択」タグがつけられた公開リストは19あり、それぞれのリストには「決断」や「意思決定」などのタグも付けられています。それらの共起タグを重複ありで洗い出し、登場回数の多い順に5つ取り出すということ。

        SELECT COUNT(t3.term_id) AS count, t3.term_id, t3.name
        FROM wp_terms AS t
        INNER JOIN wp_term_taxonomy AS tt ON t.term_id=tt.term_id AND taxonomy='post_tag'
        INNER JOIN wp_term_relationships AS r ON tt. term_taxonomy_id=r.term_taxonomy_id
        INNER JOIN wp_posts AS p ON r.object_id=p.ID AND p.post_status='publish' AND p.post_type='post'
        INNER JOIN wp_term_relationships AS r2 ON p.ID=r2.object_id
        INNER JOIN wp_term_taxonomy AS tt2 ON r2.term_taxonomy_id=tt2.term_taxonomy_id AND tt2.taxonomy='category'
        INNER JOIN wp_terms AS t2 ON tt2.term_id=t2.term_id AND t2.name='list'
        INNER JOIN wp_term_relationships AS r3 ON p.ID=r3.object_id
        INNER JOIN wp_term_taxonomy AS tt3 ON r3.term_taxonomy_id=tt3.term_taxonomy_id AND tt3.taxonomy='post_tag'
        INNER JOIN wp_terms AS t3 ON tt3.term_id=t3.term_id AND t3.term_id != (表示タグのID)
        WHERE t.term_id=(表示タグのID)
        GROUP BY t3.term_id
        ORDER BY count DESC
        LIMIT 5

        このSQL文を発行し、得られた結果を下記のようにHTMLに組み立てて返します。

            $coused_tags = $wpdb->get_results( $sql );
            foreach( $coused_tags as $c ) {
                $desc[] = '<a href="'.get_tag_link( $c->term_id ).'" title="'.$c->count.'">'.$c->name.'</a>';
            }
        
            return 'このタグと共に使われていることが多いタグ<br>'.implode( ' ', $desc );
        

        関数を使う場合

        関数を使う場合はいくつかのステップが必要です。

        1. 表示しようとしているタグが付けられている投稿群を取得する

        $wp_query がタグの付いた投稿群を最初のページ分だけは保持しているので、ページングが無ければ $wp_query から投稿IDの一覧を取得する。ページングがあれば新規にクエリを発行して投稿IDの一覧を取得する。

            $tag_id = get_query_var( 'tag_id' );
        
            global $wp_query;
        
            if( $wp_query->max_num_pages == 1 ) {
                foreach( $wp_query->posts as $p ) {
                    $post_ids[] = $p->ID;
                }
            } else {
                $q = new WP_Query( array(
                    'post_type'      => 'post',
                    'post_status'    => 'public',
                    'category_name'  => 'list',
                    'tag_id'         => $tag_id,
                    'posts_per_page' => -1, // 全行
                    'fields'         => 'ids', // IDのみ
                ) );
                $post_ids = $q->posts;
            }
        

        2. それらの投稿群に付いている(当該タグ以外の)タグを「重複ありで」取得する

        共起している回数を数えるため、上記の投稿群に付いているタグを重複ありで取得する必要があります。ここがわからなくてSQL文を書いたのですが、 wp_get_object_terms()関数がそのようなオプションを提供していました。

        もし共起タグが見つからなければここで処理終了。

            $coused_tags_all = wp_get_object_terms( $post_ids, 'post_tag', array(
                'fields' => 'all_with_object_id',
                'exclude' => $tag_id,
            ) );
        
            if( ! count( $coused_tags_all ) ) {
                return $description;
            }

        3. 共起タグの出現回数トップ5を取り出す

        $coused_tags_all は Term オブジェクトの配列で、画面表示に必要なタグ名称を持っています。余計な呼び出しを減らすため、取得済みの Term オブジェクトをできるだけ活用。

        $coused_tags はタグIDをキーとする配列で、登場回数とTermオブジェクトを値に持たせています。

        // タグの出現回数を数える
        $coused_tags = array();
        foreach( $coused_tags_all as $c ) {
            if( ! isset( $coused_tags[$c->term_id] ) ) {
                $coused_tags[$c->term_id] = array( 'count' => 1, 'obj' => $c );
            } else {
                $coused_tags[$c->term_id]['count']++;
            }
        }
        
        // 出現回数の降順でソート
        uasort ( $coused_tags , function ($a, $b) {
            return $b['count'] - $a['count'];
        } );
        
        // 最大5つを取り出す
        $coused_tags = array_slice( $coused_tags, 0, 5, true );

        4. 表示するHTMLを作成する

        foreach( $coused_tags as $k => $v ) {
            $desc[] = '<a href="'.get_tag_link( $k ).'" title="'.$v['count'].'">'.$v['obj']->name.'</a>';
        }
        
        return '「'.urldecode( get_query_var( 'tag' ) ).'」と共に使われていることが多いタグ<br>'.implode( ' ', $desc );
      • 「絶望死」の種類

        まえがき

        世界中の富裕国では、中年の死亡率は低下し続けている。しかし近年のアメリカでは、中年の白人の死亡率が上昇している。最も増加率の高い死因がこの三つ。

        リスト

        あとがき

        アン・ケース、アンガス・ディートン 『絶望死のアメリカ』(みすず書房、2021年)より。絶望死は本書が独自に命名した類型です。

        後2者は自殺ではなくカウントされないものの、きっかけがおそらく何らかの絶望によると推察できます。たとえば薬物の過剰摂取による死亡は、意図的なものでないかぎり「事故」に分類されます。しかし、

        「その死は意図したものではなかったかもしれないが、中毒物質の使用を意図していなかったということはまったくない。したがって、致死的な薬物の過剰摂取や作用による結果は本当の意味での事故ではない」

        死因を単純に分解していっても見つけられなかったであろう問題を、ある仮説で括りなおすことによって見出すことができるという好例として収録。

        2017年に絶望死で亡くなったアメリカ人は15万8千人に及ぶとのこと。本書はアメリカで絶望死が増えている原因を広く深く探っていきます。

        • タイトル絶望死のアメリカ
        • 著者: アン・ケース(著)、アンガス・ディートン(著)、松本 裕(翻訳)
        • 出版社: みすず書房
        • 出版日: 2021-01-19
        • 科学的アナロジーの3条件

          まえがき

          『特に科学におけるアナロジーを成立させる重要な性質として、言語学者の瀬戸賢一さんは、以下の3点をあげています。 』

          リスト

          あとがき

          まえがきは、安藤 昭子 『才能をひらく編集工学 世界の見方を変える10の思考法』(ディスカヴァー・トゥエンティワン、2020年)より。

          瀬戸 賢一 『メタファー思考』(講談社、1995年)からの引用とのこと。

          本書だけではややわかりづらい箇所があったので、参考文献(1)の記述も借りて要約・引用しました。

            • タイトルメタファー思考
            • 著者: 瀬戸 賢一(著)
            • 出版社: 講談社
            • 出版日: 1995-04-17

              参考文献

              (1) 轟里香. “言語学における Figure/Ground 概念の考察: 科学的メタファーの観点から.” Osaka Literary Review 34 (1995): 134-146.

            • 感情史の4つのアプローチ

              まえがき

              感情史(歴史学における感情の研究)の四つの方法論とは。

              リスト

              あとがき

              バーバラ・H.ローゼンワイン、リッカルド・クリスティアーニ 『感情史とは何か』(岩波書店、2021年)より。リストは本文をわたしの理解に沿ってまとめました。かなり粗いうえに的を外している部分があるかも。ラベルの次のカッコは、各アプローチの主要な提唱者です。

              感情史という学術領域があるんですね。まったく違った方向からの見立てがあって面白い。

              著者の一人は第三項目の提唱者なので、いきおいその部分の記述が長く、自説を推している雰囲気がにじみ出ていました。

              • タイトル感情史とは何か
              • 著者: バーバラ・H.ローゼンワイン(著)、リッカルド・クリスティアーニ(著)、伊東 剛史(翻訳)、森田 直子(翻訳)、小田原 琳(翻訳)、舘 葉月(翻訳)
              • 出版社: 岩波書店
              • 出版日: 2021-01-12
              • 自殺に至る6ステップ

                まえがき

                『彼のモデルでは、自殺する人間は自殺に至るいくつかのステップを順に踏んで移動してゆき、その移動ごとに危険度も増してゆく。(略)自殺思考の段階を行きつ戻りつすることもあるし、原理的にはどの時点でもそこから降りてしまうことも可能である。しかし、段階を進むにつれて、降りるのは難しくなる。』

                リスト

                あとがき

                まえがきを含めて、ジェシー・ベリング 『ヒトはなぜ自殺するのか』(化学同人、2021年)より。リストは本文を要約して作成しました。

                まえがきの「彼」は、社会心理学者のロイ・バウマイスター (Wikipedia)。著者が彼の「自己逃避としての自殺」(1) という論文を読んで実施した彼へのインタビューにほぼ一章が割かれています。

                インタビューの終盤では、こういったプロセスを知ることは自分の問題を別の角度から見る助けになると述べています。

                • タイトルヒトはなぜ自殺するのか
                • 著者: ジェシー・ベリング(著)、鈴木光太郎(翻訳)
                • 出版社: 化学同人
                • 出版日: 2021-01-31

                  参考文献

                  (1) Baumeister, Roy F. “Suicide as escape from self.” Psychological review 97.1 (1990): 90.

                • あるカテゴリーの公開投稿で使われている、あるタクソノミー下のタームの総数を数える

                  公開リストの中で引用している書籍の総数を数えた。

                  • 公開リストとは
                    • post_type が post
                    • post_status が publish
                    • カテゴリー が list
                  • 引用している書籍とは
                    • asin タクソノミー 下にあるターム

                  標準関数を組み合わせると次のような手順で取得できる。

                  $q = new WP_Query( array(
                  	'post_type'      => 'post',
                  	'category_name'  => 'list',
                  	'post_status'    => 'publish',
                  	'posts_per_page' => -1,
                  	'fields'         => 'ids',
                  	'tax_query' => array( // 不要だが母集団を小さくするため
                  		array( 'taxonomy' => 'asin',
                  			'operator' => 'EXISTS' ),
                      ),
                  ) );
                  
                  // ids: 投稿に付けられたASINタームの総数(重複無し)
                  $terms = wp_get_object_terms( $q->posts, 'asin', [ 'fields' => 'ids' ]  );
                  $term_count = count( $terms );	
                  
                  // all_with_object_id: 投稿に付けられたASINタームの総数(重複あり、延べ引用数)
                  $terms = wp_get_object_terms( $q->posts, 'asin', [ 'fields' => 'all_with_object_id' ]  );
                  $term_count_total = count( $terms );

                  ここで $term_count がASINターム数つまり本の数、$term_count_total が延べ引用数ということになる。

                  ただ、2つの数字を得るために千の要素を超える配列を3つも経由するのはスマートでない気がするので、SQL文で取得することを試みた。

                  asinタクソノミー下のタームが関連付けられている公開投稿を探し出し、その投稿がlistカテゴリ下にあることを確認しなければならない。

                  データベース構造(WordPress Codex 日本語版)を見ながら愚直に INNER JOIN していった。このSQL文の結果と上の関数の結果が同じで、いくつか書籍を絞ってテストしてみても正しい結果が取得できたので、きちんと動いている模様。

                  SELECT COUNT(DISTINCT t.term_id) AS total, COUNT(t.term_id) AS c_total
                  FROM wp_terms AS t
                  INNER JOIN wp_term_taxonomy AS tt ON t.term_id = tt.term_id AND tt.taxonomy = 'asin'
                  INNER JOIN wp_term_relationships AS r ON tt.term_taxonomy_id = r.term_taxonomy_id
                  INNER JOIN wp_posts AS p ON r.object_id = p.ID AND p.post_status = 'publish' AND p.post_type = 'post'
                  INNER JOIN wp_term_relationships AS r2 ON p.ID = r2.object_id
                  INNER JOIN wp_term_taxonomy AS tt2 ON r2.term_taxonomy_id = tt2.term_taxonomy_id AND tt2.taxonomy = 'category'
                  INNER JOIN wp_terms AS t2 ON tt2.term_id = t2.term_id AND t2.name = 'list'

                  備忘のため解説。

                  1. ターム(本)の数を数える。一冊の本が複数の投稿に属していることがあるので、本の数 (total) と延べ引用数 (c_total すなわち cumulative total) を取得する。
                  2. タームが保存されているのは wp_terms テーブル。
                  3. asin タクソノミーに属するタームだけを抽出する。
                  4. それらのタームが関連付けられている投稿を探し出すために、wp_term_relationships テーブルを経由する。
                  5. タームが関連付けられている投稿のうち、投稿ステータスが公開かつ投稿タイプが投稿のものだけを抽出する。
                  6. それらの投稿のカテゴリを探し出すために、wp_term_relationships テーブルを経由する。
                  7. それらの投稿に関連付けられたタームのうち category タクソノミー下にあるものだけを抽出する。
                  8. その category タクソノミー下のタームが list であるものだけを抽出する。

                  下は、このSQL文を組み立てるときに頭の整理に使った表。個人的にはわかりやすく作れたのでこれも備忘のためにメモ。# 列の数字が上のSQL文の行番号に相当する。新しいテーブルを ON(太字のセル=その直上のセル) で JOIN したうえで、太字セルの右セルのAND句で絞り込んで……を重ねていくイメージ。

                  #ASJOINANDJOINJOINANDJOINJOINANDJOINAND
                  wp_termstterm_id
                  3wp_term_taxonomyttterm_idtaxonomy = asinterm_taxonomy_id
                  4wp_term_relationshipsrterm_taxonomy_idobject_id
                  5wp_postspIDpost_status = publish
                  AND
                  post_type = post
                  ID
                  6wp_term_relationshipsr2object_idterm_taxonomy_id
                  7wp_term_taxonomytt2term_taxonomy_idtaxonomy = categoryterm_id
                  8wp_termst2term_idname = list

                  ちなみに、絞り込み部分をすべて WHERE句にまとめても同じ結果が得られるがずいぶんわかりづらい。

                  SELECT COUNT(DISTINCT t.term_id) AS total, COUNT(t.term_id) AS c_total
                  FROM wp_terms AS t
                  INNER JOIN wp_term_taxonomy AS tt ON t.term_id = tt.term_id
                  INNER JOIN wp_term_relationships AS r ON tt.term_taxonomy_id = r.term_taxonomy_id
                  INNER JOIN wp_posts AS p ON r.object_id = p.ID
                  INNER JOIN wp_term_relationships AS r2 ON p.ID = r2.object_id
                  INNER JOIN wp_term_taxonomy AS tt2 ON r2.term_taxonomy_id = tt2.term_taxonomy_id
                  INNER JOIN wp_terms AS t2 ON tt2.term_id = t2.term_id
                  WHERE tt.taxonomy = 'asin' AND p.post_status = 'publish' AND p.post_type = 'post' AND tt2.taxonomy = 'category' AND t2.name = 'list'
                • WordPress: add_action() に変数を渡す

                  LOTDの配信メールをテキストとHTMLの両方を含むマルチパートにするために検索したところ、 phpmailer_init というアクションフックが見つかった。

                  do_action_ref_array( 'phpmailer_init', PHPMailer $phpmailer )

                  $phpmailer のAltBody メンバーにテキストをセットすればよいらしい。$phpmailer は参照渡しされるので、返り値を送れないアクションフックでもセットできる。

                  ただ、アクションは外からの変数を受け取れない。

                  $text_body = (組み立てたテキスト)
                  add_action( 'phpmailer_init', 'lf_altbody', 10, 1 );
                  function lf_altbody( $phpmailer ) {
                      // $text_body をどうやって受け取る?
                  }

                  これは次のようにアクションを無名関数とすることで渡せることがわかった。

                  $text_body = (組み立てたテキスト)
                  add_action( 'phpmailer_init', function( $phpmailer ) use( $text_body ) {
                      $phpmailer->AltBody = $text_body;
                  }, 10, 1 );
                • 定期/定時処理

                  定期処理

                  仮に「最近読まれているリスト」を1時間ごとに更新するとする。通常は次のように transient の寿命を1時間としておけばよい。

                  function lf_get_poplists() {
                      if ( ! $lists = get_transient( 'LF_POPLISTS' ) {
                  
                          // リスト取得処理
                          $lists = ...
                  
                          set_transient( 'LF_POPLISTS', $lists, HOUR_IN_SECONDS );
                      }
                      return $lists;
                  }

                  この更新を、ユーザーアクセスに依存せず定期的に実行するようにしてみた。上記のリスト取得処理部分を lf_set_poplists() という関数に切り出したうえで、1時間おきに実行するためにスケジュール登録する。

                  // フック名+引数がキーとなるので array()も明示しておく
                  if( ! wp_next_scheduled( 'lf_event_poplists', array() ) ) {
                      // 第5引数を true にするために第4引数に array() を設定している
                      if( ! $ret = wp_schedule_event( time(), 'hourly', 'lf_event_poplists', array(), true ) ) {
                          // エラー処理
                      }
                  }
                  
                  // スケジュール登録したフックとアクションを関連づける
                  add_action('lf_event_poplists', 'lf_set_poplists', 10, 0);
                  
                  // アクションの実行
                  function lf_set_poplists() {
                  
                      // リスト取得処理
                      $lists = ...
                  
                      set_transient( 'LF_POPLISTS', $lists );
                  }
                  • wp_schedule_event() がエラー時に WP_Error オブジェクトを返すようにするためには、第5引数を true にしなければならない(デフォルトは false)。となると第4引数も何かをセットしなければならず、デフォルト値の array() をセット。イベントを識別するキーはフックと引数なので、wp_next_scheduled() にも敢えて array() をセットした。
                  • フック(上の例では ‘lf_event_poplists’)というものは、対応する do_action( ‘lf_event_poplists’ ) が必要なのだと思っていた。しかしスケジューラー内にフックを起動する機構があるので不要。
                  • アクションに渡す引数は array() にくるむ必要がある。以下はサンプル。
                  // 引数あり(引数の数が決まっている場合)
                  if ( ! wp_next_scheduled( 'lf_event', array( 'a', 'b' ) ) ) {
                      if ( ! $ret = wp_schedule_event( time(), 'hourly', 'lf_event', array( 'a', 'b' ), true ) ) {
                          // エラー処理
                      }
                  }
                  add_action( 'lf_event', 'lf_action', 10, 2 );
                  function lf_action( $arg1, $arg2) {
                  }
                  
                  // 引数あり(引数の数が決められない場合)
                  if ( ! wp_next_scheduled( 'lf_event', array( array( 'a', 'b' ) ) ) ) {
                      if ( ! $ret = wp_schedule_event( time(), 'hourly', 'lf_event', array( array( 'a','b ') ), true ) ) {
                          // エラー処理
                      }
                  }
                  add_action( 'lf_event', 'lf_action', 10, 1 );
                  function lf_action( $arg ) {
                  }

                  定時処理

                  LOTD(今日のリスト)を入れ替えるプログラムは毎日0:01に走らせている。現在はサーバーの cron から起動しているが、これを WordPress のスケジュールシステムに乗せるためには次のようにして起動時刻を指定する必要がある(実際に起動するのは、この指定時刻以降に wp_cron() が起動された時刻になる)。

                  $etime = new DateTimeImmutable( 'tomorrow 0:01', wp_timezone() );
                  if ( ! wp_next_scheduled( 'lf_event', array() ) ) {
                      if ( ! $ret = wp_schedule_event( $etime->getTimeStamp(), 'daily', 'lf_event', array(), true ) ) {
                      }
                  }