Ruby
JavaScript
C#
Groovy
C++11

アロー演算子の書式比較

More than 1 year has passed since last update.


Introduction

Rubyのラムダ式でアロー演算子を覚えて使うようになりました.そんなある日,Groovyに手を出したのですが,アロー演算子によるメソッド定義の書式がRubyと異なるので「?」となって,色々な言語のアロー演算子の書式について調べてみました.

調べてみると,「ラムダ式」「アロー演算子」「アロー関数」「クロージャ」など呼び方も仕組みもさまざまであり,同じものとして扱って良いのか悩みましたが,せっかく調べたので公開します.この投稿内容では,『「=>」か「->」を使ってメソッド(のようなもの)を定義して変数に代入し,(メソッドのように)実行できる』ものを対象としています.

他にもアロー演算子によりメソッド(のようなもの)を記述できる言語はありますが,短時間で調べたので抜けがあります.ご指摘いただければ徐々に改訂していきたいと思っています.できればサンプルプログラムとともにご指摘いただけると助かります.

Qiitaの制限でタグが5つまでしか付けられないようです.記した内容の先頭から5つまでをタグに反映させています.ご容赦くだだい.


非altJS系


Ruby

# 引き数が1つ

foo = -> ( x ) { x * x }
foo[ 5 ] # => 25

# 以下のように呼び出すことも可能です
foo.( 5 ) # => 25
foo.call 5 # => 25

# ()括弧は省略可能
foo2 = -> x { x * x}
foo2[ 5 ] # => 25

# 引き数が2つ
bar = -> ( a, b ) { a * b }
bar[ 2, 3 ] # => 6

# ()括弧は省略可能
bar2 = -> a, b { a * b }
bar2[ 2, 3 ] # => 6

# 引き数なし
baz = -> { print "test" }
baz[] # => test


Groovy

// 引き数が1つ

foo = { x -> x * x }
foo 5 // => 25

// 引き数が1つの場合itで代用可能
foo2 = { it * it }
foo2 5 // => 25

// 引き数が2つ
bar = { a, b -> a * b }
bar 2, 3 // => 6

// 引き数なし
baz = { println "test" }
baz() // => test


JavaScript(Firefoxで確認)

当初,「パラメータが1つでも()を省略できない」「計算結果を返すだけでもreturnを省略できない」など誤った内容を書いておりましたが,修正しました.hikaru_oao様,ご指摘ありがとうございました.

// 引き数が1つ

var foo = ( x ) => { return x * x; };
foo( 5 ); // => 25

// ()括弧は省略可能
var foo2 = x => { return x * x; };
foo2( 5 ); // => 25

// 計算結果を返すだけであればreturnと{}括弧は省略可能セットで省略可能
var foo3 = x => x * x;
foo3( 5 ); // => 25

// 引き数が2つ
var bar = ( a, b ) => { return a * b };
bar( 2, 3 ); // => 6

// 計算結果を返すだけであればreturnと{}括弧はセットで省略可能
var bar2 = ( a, b ) => a * b;
bar2( 2, 3 ); // => 6

// 引き数なし
var baz = () => { console.log( "test" ); };
baz(); // => test

// {}括弧は省略可能
var baz2 = () => console.log( "test" );
baz2(); // => test


C++11

通常,C/C++でアロー演算子と言えば(構造体ポインタ)->(メンバ変数)のように用いますが,ここではラムダ式の記述に用いてます.(C++は何で同じ記号を割り当てているのだろう?と言う疑問は残りますが・・・)

当初,返り値の型指定が抜けているなど誤った内容を記載しておりましたが,修正しました.yohhoy様,ありがとうございました.

引き数無しかつ戻り値無しの場合を追記しました.nekko1119様,ありがとうございました.

// 引き数が1つ

auto foo = [](int x) -> int { return x * x; };
foo( 5 ); // => 25

// 引き数が2つ
auto bar = [](int a, int b) -> int { return a * b; };
bar( 2, 3 ); // => 6

// 引き数なし
auto baz = []() -> const char* { return "test"; };
baz(); // => test

// 引き数なし,返り値無し
auto quz = []() -> void { std::cout << "test" << std::endl; };
qux(); // => test

// 引き数なし,返り値無しの省略形
auto qux2 = [] {std::cout << "test" << std::endl; };
qux2(); // => test

参考:2013-05-16 C++14 に追加(ry ライブラリ編の公開予定は未定です


C# (コンパイル・実行などは未確認です)

// 引き数が1つ

delegate int Foo( int x );
Foo foo = x => { return x * x; };
foo( 5 ); // => 25

// 引き数が2つ
delegate int Bar( int a, int b );
Bar bar = ( a, b ) => { return a * b; };
bar( 2, 3 ); // => 6

// 引き数なし
delegate void Baz();
Baz baz = () => { Console.WriteLine( "test" ); };
baz(); // => test

参考:ラムダ式 (C# プログラミング ガイド)


Java8

Java8からlambdaが導入されたので,追記しました.ラムダ式の引き数が2つまでであれば,Interfaceが用意されているので,以下のように使用できます.詳しくは参考ページをご覧ください.他の言語と比べ,実行時にメソッド名に相当するものが必要となる点が大きく異なります.

import java.util.function.*;

class Test {
public static void main( String[] args ) {
// 引き数が1つ
IntUnaryOperator foo = ( x ) -> { return x * x; };
System.out.println( foo.applyAsInt( 5 ) ); // => 25

// ()括弧は省略可能
IntUnaryOperator foo2 = x -> { return x * x; };
System.out.println( foo2.applyAsInt( 5 ) ); // => 25

// 計算結果を返すだけであればreturnと{}括弧はセットで省略可能
IntUnaryOperator foo3 = x -> x * x;
System.out.println( foo3.applyAsInt( 5 ) ); // => 25

// 引き数が2つ
IntBinaryOperator bar = ( a, b ) -> { return a * b; };
System.out.println( bar.applyAsInt( 2, 3 ) ); // => 6

// 計算結果を返すだけであればreturnと{}括弧はセットで省略可能
IntBinaryOperator bar2 = ( a, b ) -> a * b;
System.out.println( bar2.applyAsInt( 2, 3 ) ); // => 6

// 引き数なし
Runnable baz = () -> { System.out.println("test"); };
baz.run(); // => test

// 実行内容が一文であれば{}括弧は省略可能
Runnable baz2 = () -> System.out.println("test");
baz.run(); // => test
}
}

参考:[java8]Java8 Lambdaの文法拡張まとめ

ラムダ式の引き数が3つ以上になる場合は,以下のようにInterfaceを作成して使用するようです.メソッド名(に相当する箇所)は定義と実行が対応していれば他の名称で良いです.

// 3つのint型を受け取りint型を返すinterfaceを定義

@FunctionalInterface
interface Qux {
int apply( int x, int y, int z );
}

class Test2 {
public static void main( String[] args ) {
// 引き数が3つの場合
Qux qux = ( x, y, z ) -> { return x + y + z; };
System.out.println( qux.apply( 3, 4, 5 ) ); // => 12

// {}括弧は省略可能
Qux qux2 = ( x, y, z ) -> x + y + z;
System.out.println( qux2.apply( 3, 4, 5 ) ); // => 12
}
}


altJS系


JSX

Qiitaのシンタックスハイライトは様々な言語に対応してくれていて嬉しいのですが,残念ながらJSXには対応していないようです.対応を祈念して,JSX版はプログラム全体を記載しています.

class _Main {

static function main( args: string[] ): void {

// 引き数が1つ
var foo = ( x: number ): number -> { return x * x; };
log foo( 5 );

// 引き数が2つ
var bar = ( a: number, b: number): number -> { return a * b; };
log bar( 2, 3 );

// 引き数なし
var baz = ():void -> { log "test"; };
baz();
}
}


TypeScript

// 引き数が1つ

var foo = ( x ) => { x * x; };
foo( 5 ); // => 25

// (){}括弧は省略可能
var foo2 = x => x * x;
foo2( 5 ); // => 25

// 引き数が2つ
var bar = ( a, b ) => { a * b; };
bar( 2, 3 ); // => 6

// {}括弧は省略可能
var bar2 = a * b;
bar2( 2, 3 ); // => 6

// 引き数なし
var baz = () => { console.log( "test" ); };
baz(); // => test


CoffeeScript

CoffeeScriptでは,ラムダ式の記述に「=>」と「->」の両方が使え,thisの扱いが異なります.今回のサンプルでは生成されたJavaScriptがシンプルになるよう「->」を用いています.

# 引き数が1つ

foo = ( x ) -> x * x
foo 5 # => 25

# ()括弧は省略可能
foo2 = x -> x * x
foo2 5 # => 25

# 引き数が2つ
bar = ( a, b ) -> a * b
bar 2, 3 # => 6

# 引き数なし
baz = -> console.log "test"
baz()