Локальные функции

Локальные функции есть и в 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";
    }
  }
}

Комментарии

функция в функции

Простите меня, но этот код даже не компилится

// говорят тайпдэф может и такое...
typedef RecursiveFunction = Int -> RecursiveFunction -> Int;
class Treeproc
{
        function test()
        {
                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 );
        }
}

ошибка тактая
...hx:69: characters 30-34 : Local variable add2 used without being initialized
Если у Вас все работает, скажите пожалуйста, что Я сделал не так? Компилятор вообще - то говорит разумно... или этот пример только теория?

typedef вместо Dynamic

Тип функции в параметрах указан как Dynamic. Это потому, что каждая функция примимает вторым параметром функцию, принимающую вторым параметром функцию, принимающую вторым параметром функцию и т.д. То есть, само указание типа превращается в бесконечную рекурсию. Так что в данной ситуации ничего не остается, кроме как указать тип Dynamic.

Можно сделать так:

typedef RescursiveFunction = Int -> RescursiveFunction -> Int;

var add2 : RescursiveFunction;
var minus1 : RescursiveFunction;

add2 = function( p1 : Int, p2 : RescursiveFunction ) : Int { /*...*/ }
minus1 = function( p1 : Int, p2 : RescursiveFunction ) : Int { /*...*/ }

По крайней мере компилятор ошибок не выдаёт.

Настройки просмотра комментариев

Выберите нужный метод показа комментариев и нажмите "Сохранить установки".

Отправить комментарий

Содержание этого поля является приватным и не предназначено к показу.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <blockcode> <dd>
  • Строки и параграфы переносятся автоматически.
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • You can enable syntax highlighting of source code with the following tags: <code>, <blockcode>.

Подробнее о форматировании

CAPTCHA
Этот вопрос помогает предотвратить автоматический спам
Image CAPTCHA
Enter the characters shown in the image without spaces, also respect upper and lower case.
To prevent automated spam submissions leave this field empty.