Прежде чем переходить к возможностям функциональных объектов, хотелось бы рассмотреть варианты синтаксиса для делегатов. Кроме рассмотренного уже синтаксиса указателей на функции, в разных языках программирования существуют разные по степени удобства варианты синтаксиса для делегатоподобных объектов.
Рассмотрим передачу делегатов в функции.
В C#, прежде чем использовать делегат как аргумент функции, нужно сначала объявить тип делегата. Например так:
delegate int MyDelegate(string s);
Здесь MyDelegate - тип, значениями которого могут быть любые методы, принимающие string и возвращающие int. Использовать его можно например так:
public void Process( MyDelegate func)
{
foreach (string s in someList)
{
func(b);
}
}
В D тип делегата можно не объявлять, а объявлять прямо по месту использования.
int foo(int delegate(int x) dg)
{
return dg(10) + 1;
}
В ObjC 2.0 существует аналогичное понятие "блоки". Подобно C# (и Си) можно определить тип блока (очень похоже на указатель на функцию):
typedef void (^MyBlock)(int);
Хотя можно определять блок и без типа. Например, функция logBlock в качестве аргумента принимает другую функцию, принимающую int и возвращаюшую NSString*
void logBlock( NSString * ( ^theBlock )( int ) )
{
NSLog( @"Block returned: %@", theBlock() );
}
В скриптовых языках все несколько проще. Объявлять типы не надо (так как это языки как правило с динамической типизацией).
function foo( callback )
{
alert( callback() );
}
function bar()
{
var str = 'hello, world';
foo ( function() { return str; } );
}
bar();
В этом примере мы видим еще одну возможность функционального программирования - лямбда-функции, т.е. функции, определенные прямо в месте их использования (передачи в качестве аргумента). Такая возможность есть во многих языках с элементами ФП, но ее мы рассмотрим далее.
Во всех рассмотренных случаях (а можно рассматривать и другие языки) построение делегата достаточно громоздко и неудобно. Фактически, эти варианты мало чем отличаются от синтаксиса обычных указателей на функции, с их нагромождением скобок. Между тем, существуют и более компактные варианты. Например, Nemerle:
public static void Sort[T](ary : array[T], comparison : T * T -> int);
или Scala:
def sum(f: Int => Int, a: Int, b: Int): Int
В обоих примерах тип делегата задавался максимально просто и естестенно: "список входных параметров", "стрелочка", "список возвращаемых параметров". Ничего лишнего, все максимально наглядно и прозрачно. Именно такой вариант синтаксиса и взят за основу в Neo.
Рассмотрим передачу делегатов в функции.
В C#, прежде чем использовать делегат как аргумент функции, нужно сначала объявить тип делегата. Например так:
delegate int MyDelegate(string s);
Здесь MyDelegate - тип, значениями которого могут быть любые методы, принимающие string и возвращающие int. Использовать его можно например так:
public void Process( MyDelegate func)
{
foreach (string s in someList)
{
func(b);
}
}
В D тип делегата можно не объявлять, а объявлять прямо по месту использования.
int foo(int delegate(int x) dg)
{
return dg(10) + 1;
}
В ObjC 2.0 существует аналогичное понятие "блоки". Подобно C# (и Си) можно определить тип блока (очень похоже на указатель на функцию):
typedef void (^MyBlock)(int);
Хотя можно определять блок и без типа. Например, функция logBlock в качестве аргумента принимает другую функцию, принимающую int и возвращаюшую NSString*
void logBlock( NSString * ( ^theBlock )( int ) )
{
NSLog( @"Block returned: %@", theBlock() );
}
В скриптовых языках все несколько проще. Объявлять типы не надо (так как это языки как правило с динамической типизацией).
function foo( callback )
{
alert( callback() );
}
function bar()
{
var str = 'hello, world';
foo ( function() { return str; } );
}
bar();
В этом примере мы видим еще одну возможность функционального программирования - лямбда-функции, т.е. функции, определенные прямо в месте их использования (передачи в качестве аргумента). Такая возможность есть во многих языках с элементами ФП, но ее мы рассмотрим далее.
Во всех рассмотренных случаях (а можно рассматривать и другие языки) построение делегата достаточно громоздко и неудобно. Фактически, эти варианты мало чем отличаются от синтаксиса обычных указателей на функции, с их нагромождением скобок. Между тем, существуют и более компактные варианты. Например, Nemerle:
public static void Sort[T](ary : array[T], comparison : T * T -> int);
или Scala:
def sum(f: Int => Int, a: Int, b: Int): Int
В обоих примерах тип делегата задавался максимально просто и естестенно: "список входных параметров", "стрелочка", "список возвращаемых параметров". Ничего лишнего, все максимально наглядно и прозрачно. Именно такой вариант синтаксиса и взят за основу в Neo.
No comments:
Post a Comment