Skip to content

Instantly share code, notes, and snippets.

@leque
Last active August 21, 2018 09:40
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save leque/3626148 to your computer and use it in GitHub Desktop.
Save leque/3626148 to your computer and use it in GitHub Desktop.
2012/08/26 Java基礎勉強会 at 名古屋 資料

return の話

  • 無名関数と return で小話を一席

自己紹介

  • Twitter: dico_leque
  • 趣味 Schemer
  • お仕事で Scala とか OCaml とか F# とか Java とか

Java

public <T> boolean contains(T x, T[] arr) {
    for (T elem : arr) {
        if (elem.equals(x))
            return true;
    }
    return false;
}
  • 無名関数を使って同じように書こうとするとどうなる?
  • JSR 335: Lambda Expressions for the JavaTM Programming Language の話は最後に

Scala

def contains[T](x : T, ys : List[T]) : Boolean = {
  ys.foreach { y =>
    if (y == x)
      return true
  }
  return false
}
  • { arg => body } で無名関数。無名関数は body の部分で最後に評価した式の値を返す

  • returncontains から返る

  • return 先のメソッドのアクティブな呼び出しが複数あっても return 先は字面で決まる。レキシカルスコープ

  • 無名関数をただのブロックっぽく使える

  • Java レベルでは……

    • 無名関数ひとつごとに scala.runtime.AbstractFunction1 のサブクラスな 無名クラスのインスタンスをひとつ
    • return true の方は、 contains の呼び出しごとに Object k を作り、 foreach の内側の return はそれをマーカにした例外を throwscala.runtime.NonLocalReturnControl exncatch して exn.key() == k ならそこで return exn.value
  • 単純に例外を throw するだけだと dynamic scope っぽくなる

  • return false の方はただの return になる

  • return を含む無名関数をコンテキスト外に取り出して実行すると NonLocalReturnControl 例外が見える(ただし型安全なプログラムを書いているかぎり そういうことはできない(と思う))

  • 実際に書く場合は return は使わず再帰で

def contains[T](x : T, ys : List[T]) : Boolean = {
  ys match {
    case Nil => false
    case y::rest =>
      if (y == x)
        true
      else
        contains(x, rest)
  }
}

Smalltalk

Collection>>contains: anObject
  self do: [:each| each = anObject ifTrue: [^true]].
  ^false
  • [:arg| ... ] で無名関数(BlockContext
  • ^expr でメソッドから値を返す。無名関数は最後に評価した式の値を返す
  • Scala と同じような感じ
  • 条件分岐にも無名関数を使う e.g. Boolean>>ifTrue:
  • 制御構造はすべてメソッド呼び出し + ブロックで書く
  • ブロックを外に持ち出してコンテキスト外で ^ すると BlockCannotReturn エラー
  • self はブロックの外側の self になる

Self

_AddSlots: (|
  contains: anObject = (
    do: [|:each.:idx| anObject = each ifTrue: [^true]].
    ^false
  )
|)
  • プロトタイプオブジェクト指向言語
  • メソッドとブロックは別。メソッド: ( ... )、 ブロック [ ... ]
  • メソッドも first-class
  • ^ はメソッドから抜ける
  • あとは Smalltalk と同じ

JavaScript

  • プロトタイプオブジェクト指向言語
  • function しかない(←→ Self)
  • returnfunction から抜ける
  • 途中脱出するなら throw (Scala と同じような感じで)
  • return しないと値を返せないから仕方ない。
  • 他にも内側の function から外側の this を参照したつもりでハマる

OCaml

  • そもそも return がない
  • throw Exit でほげほげ(ただし複数の Exit を区別できない。 dynamic-scope)
  • ローカルモジュールで毎回例外を定義してそれを投げればどの return か区別できる (Janestreet Core https://bitbucket.org/yminsky/ocaml-core/wiki/Homewith_return)
  • そもそも return 的なものを使わず再帰で書く

Haskell

  • 大域脱出のような計算効果 (computational effect) を扱いたければモナドで
  • Error モナドとか
  • そもそも return 的なものを使わず再帰で書く

Scheme

  • call/cc で手続きの戻り値を待つ継続を捕捉しておいて、 return したいところで その継続を起動する
  • 継続はふつうの first-class object なので、 return したいところまで 変数に入れて持っていけばよい
  • call/cc (multi-shot continuation) は重いので、 本当は call/ec (escape continuation, one-shot continuation) を使った方がよい。
  • returnthrow もただの脱出継続
  • そもそも return 的なものを使わず再帰で書く

CommonLisp

  • return でもっとも内側の block から返る
  • return-from name valreturn 先を指定できる
  • return は lexical-scope (←→ catch, throw
  • 関数型とかどうでもいいので loop から return する(個人差があります)

JSR 335

  • thissuper は外側のコンテキストを見る
  • return は無名関数からの return
  • break / continue は non-local jump しない

まとめ

  • プログラムの意味は lexical (静的)に決まった方がうれしい

  • Scala 等の return 風の挙動を例外で再現するのは少しだけ面倒

  • 無名関数をふつうの block のように使いたい場合は return は 外に効いてくれた方がうれしい

  • JavaScript は functionmethod を分けた方がよかったのでは

  • でも return 必須な言語では function からどう返れば……

  • JSR-335 は JavaScript 風の this みたいなハマり方はしない

  • return の場所によってコストが変わったりしないのは無難

  • returnthrowbreakcontinue もただの継続の起動

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment