Блог разработчиков

функциональное программирование на Python

Макс Ищенко
Опубликовано 19.01.2006 в Разработка, статьи

Несмотря на изначально убогую форму ламбда-функций а затем и deprecation типичных функционалов map, filter, reduce функциональное программирование на Python развивается и довольно успешно. Может быть потому, что функции с самого начала были first-class citizens.

Про list comprehensions уже наверное все слышали, как и про нормальную поддержку замыканий (closures). Сюда же можно отнести и декораторы, появившиеся в 2.4 и являющиеся, по сути, средством создания замыканий вокруг объектов-функций.

Библиотечные модули тоже развиваются, в Python 2.3 появился itertools (я им, правда никогда не пользовался), а в 2.5 обещают partial function application. Выглядеть это будет так:

def log (level, message, subsystem):
print '%s: %s' % (subsystem, message, level)
...

server_log = partial(log, subsystem='server')
debug = partial(log, DEBUG)

server_log(INFO, 'test message')
debug('another test', 'ui')

Функция partial, кстати, получила отдельный библиотечный модуль - functional, так что далі буде. ;-)

P.S.: Конечно, partial имеет мало общего (за исключением внешнего сходства) с function currying из ФП, но все равно забавно. Надо бы еще о generic dispatch написать - это даже более интересно.

top of hotblogs.org.ua

1 звезда2 звезды3 звезды4 звезды5 звезд (Еще не оценили)
Загрузка ... Загрузка ...

Понравилась статья? Подпишись на обновления по RSS/E-mail

Подписаться, не оставляя комментарий

Все комментарии (8) к “функциональное программирование на Python”

  1. motus говорит:

    ну, насчет нормальной поддержки замыканий ты погорячился, мне кажется.

  2. Max говорит:

    насчет нормальной поддержки замыканий ты погорячился

    Почему? В моем представлении closure == функция + контекст. Ну так оно есть:

    >>> def shamba(n):
    … def f():
    … print n
    … return f

    >>> g = shamba(42)
    >>> g()
    42

  3. motus говорит:

    Так-то оно так, да не совсем:

    >>> def outer(x):
    ... acc = 0
    ... def inner(y):
    ... acc += y
    ... return acc
    ... map(inner, x)
    ... return acc
    ...
    >>> outer([1,2,3,4])
    Traceback (most recent call last):
    File “”, line 1, in ?
    File “”, line 6, in outer
    File “”, line 4, in inner
    UnboundLocalError: local variable ‘acc’ referenced before assignment

    ах, да:

    >>> def outer(x):
    … acc = [0]
    … def inner(y):
    … acc[0] += y
    … return acc[0]
    … map(inner, x)
    … return acc[0]

    >>> outer([1,2,3,4])
    10
    >>>

    Yuck.

  4. Max говорит:

    Ну, тут да, есть момент. Хотя я всегда рассматривал вложенный контент как read-only.

  5. motus говорит:

    ну, так и анонимные классы в Java можно замыканиями обозвать :)
    имно корень зла в том, как создаются новые переменные в питоне.
    в ruby, кстати, те же проблемы:

    closure3.rb(main):001:0> acc = 10000
    => 10000
    closure3.rb(main):002:0> def outer(arr)
    closure3.rb(main):003:1> acc = 0
    closure3.rb(main):004:1> def inner(x)
    closure3.rb(main):005:2> acc += x
    closure3.rb(main):006:2> end
    closure3.rb(main):007:1> arr.each(&method(:inner))
    closure3.rb(main):008:1> acc
    closure3.rb(main):009:1> end
    => nil
    closure3.rb(main):010:0> p outer([1,2,3,4])
    NoMethodError: undefined method `+’ for nil:NilClass
    from closure3.rb:5:in `inner’
    from closure3.rb:10:in `to_proc’
    from closure3.rb:7:in `outer’
    from closure3.rb:10
    from :0
    closure3.rb(main):011:0>

    только, в отличие от питона, в ruby это, похоже, вообще не лечится, т.е. вложенные функции замыканиями не являются. в то же время, блоки в ruby ведут себя почти правильно:

    closure4.rb(main):001:0> acc = 10000
    => 10000
    closure4.rb(main):002:0> def outer(x)
    closure4.rb(main):003:1> acc = 0 # Fails if we comment this line out
    closure4.rb(main):004:1> x.each { |x| acc += x }
    closure4.rb(main):005:1> acc
    closure4.rb(main):006:1> end
    => nil
    closure4.rb(main):007:0> p outer([1,2,3,4]), acc
    10
    10000
    => nil

    такие дела.

  6. motus говорит:

    вдогонку - попробовал то же самое на перле. анонимные sub-ы, похоже, работают правильно, а вот именованые sub-ы ведут себя довольно коварно, особенно если не использовать ключик -w:

    motus@smatusev-1:~/work/svn/misc$ cat closure3.pl
    #!/usr/bin/perl -w
    use strict;
    my $acc = 10000;
    sub outer {
    my $acc = shift;
    sub inner {
    $acc += $_;
    return $_;
    }
    map( &inner, @_ );
    return $acc;
    }
    print "It kinda works: ", outer(0,1,2,3,4), " $acc\n";
    print "But not quite: ", outer(100,1,2,3,4), " $acc\n";

    motus@smatusev-1:~/work/svn/misc$ ./closure3.pl
    Variable "$acc" will not stay shared at ./closure3.pl line 10.
    It kinda works: 10 10000
    But not quite: 100 10000

    страшновато :)
    а вот такое работает на ура:

    motus@smatusev-1:~/work/svn/misc$ cat closure4.pl
    #!/usr/bin/perl -w
    use strict;
    my $acc = 10000;
    sub outer {
    my $acc = shift;
    my $inner = sub {
    $acc += $_;
    return $_;
    };
    map( &$inner, @_ );
    return $acc;
    }
    print outer(0,1,2,3,4), " $acc\n";
    print outer(100,1,2,3,4), " $acc\n";

    motus@smatusev-1:~/work/svn/misc$ ./closure4.pl
    10 10000
    110 10000

    вывод - идея my() в перле помогает жить. мне даже кажется, что питону и руби не хватает чего-то подобного. в конце концов, my() это почти как let в scheme.
    ну и раз я уже заговорил о scheme - вот пример совсем правильного замыкания:

    motus@smatusev-1:~/work/svn/misc$ cat closure.scm
    (define acc 10000)
    (define (outer start x)
    (let ((acc start))
    (define (inner y)
    (set! acc (+ acc y)) )
    (map inner x)
    acc ) )

    (write (list
    (outer 0 '(1 2 3 4))
    (outer 100 '(1 2 3 4))
    acc ))
    (newline)

    motus@smatusev-1:~/work/svn/misc$ guile -s closure.scm
    (10 110 10000)

    так что остальным языкам есть куда расти :)

  7. Max говорит:

    Подожди, а какой-нибудь real-world пример ты можешь привести? А то я как-то не соображу. А так - любой mutable type справляется:

    class counter:
    def update(n):
    ..

    acc = counter()

  8. motus говорит:

    я не говорю, что замыканий в питоне нет - они есть, но поскольку оператор ‘=’ в питоне одновременно производит и связывание, и присваивание, иногда приходится извращаться, т.к. питон не знает, объявляем ли мы новую переменную или изменяем значение старой.
    твой mutable type это по сути такой же трюк, как и мой пример с массивом (acc = [0]). т.е. проблема не с замыканиями как таковыми, а с тем, как они вписываются в общую картину.

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

Указать свой сайт могут только зарегистрированные пользователи. Регистрация или вход.

Архив

Вакансии rss icon

Все вакансии

Комментарии