0Pricing
Lua Academy · レッスン

部分適用

関数の引数をあらかじめ埋めます。

「部分適用」はCoddyKit上の無料Lua Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはLua Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Lua Academyコースには全4レッスンが含まれています。

部分適用とは

部分適用とは、関数の引数の一部を先に固定し、残りを後から渡すことです。必要な引数が少なくなった新しい関数が返されます。

これにより、すでに分かっている値を固定して、汎用的な関数を特化した関数に変えられます。

手動で作成する例

add関数があるとします。5を捕捉して、2つ目の数値を待つクロージャを記述すると、addFiveを作成できます。

捕捉された値は返された関数内に保持されるため、呼び出すたびに残りの引数だけを渡せば済みます。

local function add(a, b)
  return a + b
end

local function addFive(b)
  return add(5, b)
end

print(addFive(10))
print(addFive(2))

最初の引数を固定する

このパターンを一般化できます。bind1ヘルパーは関数と固定する最初の引数を受け取り、2つ目の引数を受け取る新しい関数を返します。

クロージャはfと固定した値の両方を捕捉し、呼び出されたときにすべての値を渡します。

local function bind1(f, a)
  return function(b)
    return f(a, b)
  end
end

local function mul(x, y) return x * y end
local double = bind1(mul, 2)
print(double(7))
print(double(50))

可変長引数を使う

先頭の引数を任意の数だけ固定するには、Luaの可変長引数構文...を使います。固定する引数を捕捉し、呼び出し時に後続の引数を追加します。

table.packとtable.unpack関数は、引数のリストを保存したり展開したりするのに役立ちます。

local function partial(f, ...)
  local fixed = table.pack(...)
  return function(...)
    local rest = table.pack(...)
    local args = {}
    for i = 1, fixed.n do args[i] = fixed[i] end
    for i = 1, rest.n do args[fixed.n + i] = rest[i] end
    return f(table.unpack(args, 1, fixed.n + rest.n))
  end
end

local function add3(a, b, c) return a + b + c end
local add10 = partial(add3, 10)
print(add10(2, 3))

複数の引数を固定する

partial は可変長引数を受け取るため、複数の引数を一度に固定できます。返される関数は、残りの引数だけを受け取ればよくなります。

ここでは3つの引数のうち2つをあらかじめ指定しているため、引数を1つだけ受け取る関数が残ります。

local function partial(f, ...)
  local fixed = table.pack(...)
  return function(...)
    local rest = table.pack(...)
    local args = {}
    for i = 1, fixed.n do args[i] = fixed[i] end
    for i = 1, rest.n do args[fixed.n + i] = rest[i] end
    return f(table.unpack(args, 1, fixed.n + rest.n))
  end
end

local function label(prefix, sep, value) return prefix .. sep .. value end
local tag = partial(label, "id", ":")
print(tag(42))

実践的な使い方

部分適用は、汎用的なユーティリティ関数と組み合わせると特に効果を発揮します。設定を一度だけ固定し、その特殊化した関数を何度も再利用できます。

ここでは、汎用的な greet を言語が固定された挨拶関数に変え、名前だけを受け取るようにしています。

local function greet(lang, name)
  if lang == "tr" then return "Merhaba, " .. name end
  return "Hello, " .. name
end

local function bind1(f, a) return function(b) return f(a, b) end end
local greetTr = bind1(greet, "tr")
print(greetTr("Ada"))
print(greetTr("Lin"))

部分適用とカリー化の違い

カリー化では、複数の引数を取る関数を、次の関数を返す単一引数関数の連鎖に変換します。一方、部分適用では一部の引数だけを固定し、残りは通常の呼び出しとして受け取ります。

この2つは関連していますが別のものです。カリー化は1つずつ引数を渡し、部分適用は一部の引数を先に固定します。

local function curryAdd(a)
  return function(b)
    return function(c)
      return a + b + c
    end
  end
end

print(curryAdd(1)(2)(3))

2引数関数のカリー化

小さなヘルパーを使うと、任意の2引数関数を自動的にカリー化できます。最初の引数を受け取って、2番目の引数を受け取る関数を返します。

これにより、パイプライン内で読みやすく使える、再利用可能な単一引数のビルダーを作成できます。

local function curry2(f)
  return function(a)
    return function(b)
      return f(a, b)
    end
  end
end

local function pow(base, exp) return base ^ exp end
local cpow = curry2(pow)
print(cpow(2)(10))

クロージャが仕組みを支える

部分適用した関数もカリー化した関数も、クロージャに依存しています。固定した引数は、返された関数が記憶しているキャプチャ済みのローカル値です。

返される各関数はそれぞれ固有の値をキャプチャするため、多数の特殊化したバリエーションを安全に作成できます。

local function bind1(f, a) return function(b) return f(a, b) end end
local function sub(x, y) return x - y end

local from100 = bind1(sub, 100)
local from10 = bind1(sub, 10)
print(from100(40))
print(from10(3))

使う場面

同じ先頭の引数を何度も指定して関数を呼び出す場合は、部分適用を使って重複を減らせます。

また、1つの値だけを渡すコールバックのように、より少ない引数を想定するインターフェースに関数を適合させる場合にも役立ちます。

設定済みのコールバック

部分適用は map や filter と相性がよい機能です。設定用の引数をあらかじめ指定して、コールバックにはリスト要素だけが渡されるようにできます。

これにより、動作を調整できる柔軟性を保ちながら、パイプラインをすっきりと記述できます。

local function map(t, f) local o={} for i,v in ipairs(t) do o[i]=f(v) end return o end
local function bind1(f, a) return function(b) return f(a, b) end end
local function scale(factor, x) return factor * x end

local r = map({1,2,3,4}, bind1(scale, 10))
print(table.concat(r, ", "))

理解度チェック

関連する2つの考え方の違いを思い出してください。

まとめ

部分適用では一部の引数を固定し、固定値をキャプチャするクロージャによって、残りの引数を受け取る新しい関数を返します。

可変長引数を使う partial ヘルパーはこの仕組みを一般化します。一方、カリー化は単一引数関数を連鎖させます。どちらも重複を減らし、関数をコールバックに適合させるのに役立ちます。

よくある質問

「部分適用」レッスンは無料ですか?

はい。「部分適用」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Lua Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Lua Academyコースには全4レッスンが含まれています。

「部分適用」で何を学びますか?

関数の引数をあらかじめ埋めます。 ブラウザで直接実行するハンズオンコードでLua Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Lua Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのLua Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「部分適用」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このLua Academyレッスンでコードを書いて実行できますか?

はい。すべてのLua Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. 値としての関数
  2. map、filter、reduce
  3. 部分適用
  4. 関数を合成する
← Lua Academyに戻る