AIに聞いても知識が身につかないのはなぜなのか

サポーターズで働いています、新卒2年目の@fuchio_gtです。 先日、後輩とOJT振り返りをしていた時に、AIを利用した学習方法について話しました。 その時の私のお気持ちを備忘録としてブログに残しておこうと思います。

この記事で伝えたいこと

  • アウトプットが知識を定着させる
  • 学習時には「疑問の細分化を自分で行う姿勢」が大事
  • 思考の主導権をAIに渡してはならない

はじめに

業務中、難解なコードに張り倒されることってありますよね。
Scalaを触り始めてもうすぐ2年経つっていうのに、未知の概念がわんさか出てきます。
苦しい。

そんな時はAIです。 なんでもかんでもブチこんでみて、「なにこれ」って聞けばそれなりの解答が返ってきます。 便利な世の中になりましたね。

なんか昔より知識が身についてない気がするけど、仕事は進むからヨシ!

…それでいいのか?

効率良く知識を身につけるには

一般的に、人間はインプットよりアウトプットの際に効率良く学習することが知られています。

例えば、三角関数の加法定理ってありますよね。 あれは教科書に載ってる公式を暗記するだけでもいいっちゃいいんですけど、公式の導出・証明ができる人は強く記憶に残っていると思います。 (ちなみに私は暗記していた側なので覚えていません)

ほかにも、誰かに説明するつもりでブツブツ喋ってみたり、別の概念で例えてみたり...。 方法はたくさんありますが、とにかく学習におけるアウトプットは記憶の定着にとても効果的だという話です。 つまり、業務においてもアウトプットを強く意識すると学習が捗る、ということですね。

詳しく知りたい方は『科学的根拠に基づく最高の勉強法』という本を読んでみると面白いと思います。 『世界一流エンジニアの思考法』の著者である牛尾さんも絶賛していました

実は「ググる」のがいいアウトプットになっていたんじゃないか

(いや、ググってんだからインプットじゃ〜んw)

AIが台頭するまでは、皆さん必死でググっていたはずです。 私は「ググるのが上手い人」のことをエンジニアと呼ぶのだとさえ思っていました。

ググって疑問を解消するには、以下の手順が必要です。

  • 疑問の細分化
  • 各疑問ごとに検索
  • 検索で得た抽象的な情報を統合
  • 具体的な事象に落とし込む

この抽象と具体の行き来が、いい感じの学習負荷を脳ミソにかけてくれていたんじゃないかと。 アウトプットに近いんじゃないかという感覚があるんですよね。

具体例で話そうぜ

以下のようなScalaのコードがあるとします。 読めなくても大丈夫です。 むしろ読めない方が話がわかりやすいかも。

sealed trait Internship

case class Treasure(
  startAt: ZonedDateTime, 
  endAt: ZonedDateTime
) extends Internship

object Treasure {
  def apply(
    startAtLDT: LocalDateTime,
    endAtLDT: LocalDateTime,
    zoneId: ZoneId
  ): Treasure = {
    val startAtZDT = startAtLDT.atZone(zoneId)
    val endAtZDT = endAtLDT.atZone(zoneId)
    Treasure(startAtZDT, endAtZDT) 
  }
}

val startDateTimeZoned: ZonedDateTime = ZonedDateTime.of(2025, 8, 1, 9, 0, 0, 0, ZoneId.of("Asia/Tokyo"))
val endDateTimeZoned: ZonedDateTime = ZonedDateTime.of(2025, 8, 31, 17, 0, 0, 0, ZoneId.of("Asia/Tokyo"))

val treasure1: Treasure = Treasure(startDateTimeZoned, endDateTimeZoned)

val startDateTimeLocal: LocalDateTime = LocalDateTime.of(2025, 9, 1, 9, 0, 0, 0)
val endDateTimeLocal: LocalDateTime = LocalDateTime.of(2025, 9, 30, 17, 0, 0, 0)
val japanZone: ZoneId = ZoneId.of("Asia/Tokyo")

val treasure2: Treasure = Treasure(startDateTimeLocal, endDateTimeLocal, japanZone)

初学者がこのコードをみた時、以下のような疑問を持つんじゃないでしょうか。

  • sealedってなんや
  • case classってなんや
  • なんでcase classと同名のobjectってやつもあるんや
  • なんでapplyメソッドは1つしかないのに初期化の方法は2通りあるんや

このように疑問を細分化してから、個別にググって解消していきます。

  • sealedってなんや
    • ほーん、つけたら同一ファイル内でしか継承できなくなるんやな ↓
  • case classってなんや
    • equalsとかcopyとか使うであろう基本メソッドを自動生成してくれる便利なclassらしいな ↓
  • なんでcase classと同名のobjectってやつもあるんや
    • コンパニオンオブジェクトって言うんや、Javaでいう静的メソッド置き場としての役割っぽい ↓
  • なんでapplyメソッドは1つしかないのに初期化の方法は2通りあるんや
    • あー!case classを定義した時点でコンパニオンオブジェクトも自動生成されてるんだ、だから実はapplyメソッドが2つあるのね

そして、ググって得た抽象的な知識を組み合わせ、具体的なコードに落とし込んでいく。 ここまでして、ようやくコードが読めるようになります。

かったり〜〜〜AI(Gemini)に聞いてみようぜ

Scalaコードの解説(クリックで展開)

  1. なにこれ

  2. これはScalaというプログラミング言語のコードですね。


コードの概要

このコードは、インターンシップの期間を管理するための Treasure というデータ構造を定義しています。特に時間帯情報(タイムゾーン)を考慮して、正確な期間を表せるように設計されています。


各部分の説明

  • sealed trait Internship:

    • これは「Internship」という名前のトレイト(Javaでいうインターフェースに似ています)を定義しています。sealed と付いているので、このトレイトを継承できるクラスは、このファイル内でしか定義できません。これは、インターンシップの種類を制限したい場合に使われます。
    • 現時点では Internship を継承しているのは Treasure だけですが、もし将来的に「SummerInternship」や「WinterInternship」など、異なる種類のインターンシップを追加する場合、それらもこの Internship トレイトを継承することになるでしょう。
  • case class Treasure(...) extends Internship:

    • これは「Treasure」という名前のケースクラスを定義しています。Internship トレイトを継承しています。
    • ケースクラスはScalaでよく使われるデータ保持用のクラスで、いくつか便利な機能が自動で提供されます(例えば、インスタンスの比較、コピー、toString メソッドなど)。
    • Treasure クラスは startAtendAt という2つのフィールドを持っています。どちらも ZonedDateTime 型で、これは「日付、時刻、そしてタイムゾーン情報」を組み合わせたものです。これにより、例えば「日本時間の午前9時」と「UTCの午前9時」を区別して扱うことができます。
  • object Treasure { def apply(...) }:

    • これは Treasure ケースクラスのコンパニオンオブジェクトです。コンパニオンオブジェクトは、対応するクラスと同じ名前を持ち、クラスの静的メソッドのようなものを提供します。
    • ここでは、apply メソッドが定義されています。この apply メソッドは、LocalDateTime(タイムゾーン情報を含まない日付と時刻)と ZoneId(タイムゾーンID)を受け取って、内部で ZonedDateTime に変換し、最終的に Treasure インスタンスを生成するヘルパー関数として機能します。
    • これにより、LocalDateTime で日付と時刻を指定し、後からタイムゾーンを適用して Treasure を作成できるようになります。
  • val startDateTimeZoned: ZonedDateTime = ...val endDateTimeZoned: ZonedDateTime = ...:

    • これらは、2025年8月1日から8月31日までの期間を、最初から「Asia/Tokyo」(日本時間)のタイムゾーン付きで定義している例です。
  • val treasure1: Treasure = Treasure(startDateTimeZoned, endDateTimeZoned):

    • 上記で定義したタイムゾーン付きの日時を使って、最初の Treasure インスタンスを作成しています。
  • val startDateTimeLocal: LocalDateTime = ...val endDateTimeLocal: LocalDateTime = ...:

    • これらは、2025年9月1日から9月30日までの期間を、タイムゾーンなしの「ローカル日時」として定義している例です。
  • val japanZone: ZoneId = ZoneId.of("Asia/Tokyo"):

    • 「Asia/Tokyo」というタイムゾーンIDを定義しています。
  • val treasure2: Treasure = Treasure(startDateTimeLocal, endDateTimeLocal, japanZone):

    • LocalDateTimeZoneId を使って、Treasure コンパニオンオブジェクトの apply メソッドを呼び出し、2番目の Treasure インスタンスを作成しています。この方法は、日時をまずタイムゾーンなしで扱い、後から特定のタイムゾーンを割り当てたい場合に便利です。

まとめ

このコードは、インターンシップの期間を、タイムゾーンを考慮して正確に表現・管理するための、堅牢な方法を提供しています。特に、タイムゾーンの概念を扱う際に発生しがちな日付や時刻のずれを防ぐことを目的としていると考えられます。

Gemini鬼つええ!

「なにこれ」というプロンプトだけで、疑問の細分化から具体的な事象への落とし込みまで全部やってくれます。 もうググるなんて時代遅れなことやらなくていいっすね。

でもインプットしかしてないから身につかないんだよな。

おわりに

じゃあなんすか、AIなんて使わずにググれっていうんすか、ってわけではないです。 文明の利器から得られる恩恵は最大限に享受すべきだと思います。 幸いCARTAではGoogle Workspaceを利用しており、AI(Gemini)と会話できる環境が用意されているのですから。

一番大事なのは、疑問の細分化を自分で行う姿勢だと思っています。 自身から生まれた疑問に対する知的好奇心は、そうでないものとは比較にならないくらい高いものです。 「内発的動機づけ」ってやつですね。

その意識があれば、AIを利用した学習でもより豊かに、生産的になるんじゃないかなと思っています。 多分。

思考の主導権をAIに渡してはならない、という話でした。