Локальные функции есть и в ActionScript и вполне удобны в некоторых случаях (я, например, реализовывал с их помощью паттерн Visitor для обхода и обработки рядов таблицы). Но использование локальных функций в haXe отличается строгой проверкой типов. Ниже материал из книги "Professional haXe and Neko".
Локальные функции безымянны. При создании локальной функции ссылка на нее присваивается переменной. С-программисты могу рассматривать это как некий аналог указателям на функции.
var functionVariable = function( param : ParamType [, ...] ) : ReturnType
{
// код функции...
}
Когда локальная функция присваивается переменной, тип этой переменной определяется путем выявления типов, или же его можно указать явно. Типизация переменных, хранящих ссылки на функции, соответствует подходу, принятому в функциональных языках. Указав тип переменной, мы явно указываем, какие параметры должна принять функция и что она должна вернуть.
Переменная, хранящая ссылку на локальную функцию, описывается следующим образом:
var functionVariable : ParamOne [[-> ParamTwo] ...]-> ReturnValue;
Здесь указан тип каждого параметра функции. Параметры разделяются оператором ->. Выражение завершается типом возвращаемого значения, также отделенным оператором ->. Например:
var fun1 = function() : Void
{
// код...
}
var fun2 = function( p1 : String ) : Void
{
// код...
}
var fun3 = function( p1 : Int, p2 : Float, p3 : Int ) : String
{
// код...
return someString;
}
var fun1 : Void -> Void;
var fun2 : String -> Void;
var fun3 : Int -> Float -> Int -> String;
Главная фишка локальных функций это то, что они могут быть переданы как параметры другим функциям (методам класса или другим локальным функциям). При этом тип функции-аргумента должен быть указан в параметрах для принимающей функции.
var fun1 : String -> String;
fun1 = function( p1 : String ) : String
{
// выполнить код...
return someString;
}
var fun2 : String -> ( String -> String ) -> String;
fun2 = function( p1 : String, p2 : ( String -> String ) ) : String
{
return p2( p1 );
}
var tmp = fun2( "someString", fun1 );
Таким способом можно реализовать причудливые рекурсии. Поскольку мы может определить тип переменной, не присваивая ей функцию, то можно создать две функции, которые будут принимать в параметрах друг друга и вызывать друг друга. Это удобно для обработки древовидных структур.
typedef RecursiveFunction = Int -> RecursiveFunction -> Int;
var add2 : Int -> RecursiveFunction -> Int;
var minus1 : Int -> RecursiveFunction -> Int;
add2 = function( p1 : Int, p2 : RecursiveFunction ) : Int
{
p1 += 2;
trace( p1 );
if ( p1 < 5 ) p1 = p2( p1, add2 );
return p1;
}
minus1 = function( p1 : Int, p2 : RecursiveFunction ) : Int
{
p1--;
trace( p1 );
p1 = p2( p1, minus1 );
return p1;
}
add2( 0, minus1 );
// Вывод: 2, 1, 3, 2, 4, 3, 5
Здесь одна функция прибавляет 2 к своему аргументу, а вторая вычитает 1. Они вызывают друг друга, пока не будет достигнут определенный лимит.
Каждая функция примимает вторым параметром функцию, принимающую вторым параметром функцию, принимающую вторым параметром функцию и т.д. То есть, само указание типа превращается в бесконечную рекурсию. Есть два способа определить такой тип:
1) использовать Dynamic (но при этом потерять все плюсы строгой типизации);
2) указать алиас для типа с помощью typedef.
В данном случае мы воспользовались вторым вариантом, описав тип RecursiveFunction. Как вы можете видеть, typedef позволяет описывать типы, ссылающиеся на самих себя.
Область видимости локальной функции включает:
- статические параметры класса, в котором она определена;
- локальные переменные, определенные до нее в том же блоке;
- ее параметры;
- и локальные переменные, опредленный внутри этой функции.
class LocalFunctionVars
{
public static var myStaticVar1 : String;
public var myVar1 : String;
public static function main()
{
var myLocalVar1 : String;
var myFunction = function()
{
var innerFunction : String;
innerFunction = "haXe";
myLocalVar1 = "is";
myStaticVar1 = "really";
// выбросит ошибку компилятора
myVar1 = "amazing";
}
}
}
Здесь доступны большинство параметров класса, но не доступен не статический параметр myVar1. Доступ к таким параметрам обычно осуществляется через ссылку this, но this не доступен внутри локальной функции. Но это можно обойти с помощью небольшого хака. Благодаря тому, что локальной функции доступны локальные переменные, определенные до нее, ссылку this можно сохранить в такой переменной.
class LocalFunctionVars
{
public static var myStaticVar1 : String;
public var myVar1 : String;
public static function main()
{
var l = new LocalFunctionVars();
}
public function new()
{
var myLocalVar1 : String;
var me = this;
var myFunction = function()
{
var innerFunction : String;
innerFunction = "haXe";
myLocalVar1 = "is";
myStaticVar1 = "really";
// теперь компилируется
me.myVar1 = "amazing";
}
}
}