Как стать автором
Обновить

Комментарии 39

Применение def-функций в некоторых случаях не работает в стиле ФП.
и вот этому «ужасному» и «нарушающему» в книжке учат детей. автор что и в соседней статье про пайтон а там он честно признался что публикует главы своей книжки тут в качестве бесплатной рекламы
«в качестве бесплатной рекламы» — это ваши домыслы.

А вот свой авторский материал я считаю интересным, вот и хотел им поделиться, и надо сказать, что некоторым он нравится.
нравится или нет — это вопрос личного отношения. оценка "-1" к статье показывает что пока не понравилось в среднем большему количеству людей.

если учебник пишите — то пишите его правильно. не надо у в учебнике писать плохой код, и учить программировать криво. есть PEP8 — придерживайтесь его хотя бы. простые правила как чистить зубы по утрам. можно и не чистить, но будет вонять изо рта. в принципе вам плевать, жевать то есть чем пока что, но другие будут кривиться когда вы будете открывать рот

учебник должен учить правильному. если вам хочется творческой самореализации — то придумайте свои примеры и задачи. но синтаксис это святое и часть языка, а вы именно в него свои творческие видения запускаете. да PEP8 это формально свод рекомендаций, а не жестких правил, но именно это позволяет потом другим людям читать код и не плеваться в сторону автора. и так полно говнокода кругом, а вы еще учебник пишете о том как его сделать больше
PEP8 — это рекомендация, НЕ догма. ФП — это парадигма, которая, кстати, поддерживается языком. Если вы не хотите заниматься ФП, то вам не сюда.

Я добавил в материал книги дополнение, которое кому-то поможет взглянуть на синтаксис и возможности по-другому. По меньшей мере, ответить на вопрос, почему в языке есть функции map, reduce и filter, и как это связано с ФП.

Не стоит предписывать мне, что мне делать. Я — свободный человек, как и вы, надеюсь.

Вы выступаете тут, как окончательный арбитр. Не кажется ли вам, что такая роль — ущербна?

лол, вы описание то читали? весь запрет в том что дэбаггер не может показать имя переменной, к которой присвоена лямбда, поэтому вместо нее пишут функцию. выглядит как грязный костыль

Скажите, а причем тут отладчик, если речь об исполнимом коде, т.е. причем тут время отладки, когда речь о времени исполнения?

неверно выразился, не отладчик, а для отладки,
правило же чисто декоративное — вывод имени функции в трассировке

В питоне у функциональных подходов очень много минусов и поэтому почти никто в питоне их не применяет.


Я сам пришёл в Питон с языка где функциональные подходы были естественными и первое что начал — применять их в Питоне. И затем спустя какое-то время… отказался от этого.


Вместо фильтров, мапов, теперь использую генераторы [ x for x in bla if foo], {x: y for x, y in bla} итп


Проблемы:


  • Выравнивание кода отступами.

Питон код выравнивает отступами. При этом, как правило, полиси каждого проекта ограничивает максимальную длину строки.
А функциональный подход занимает очень много символов.


Как правило запись генератора куда компактнее каскада map-filter-lambda


  • у питона нет лямбда функций. Только лямбда-оператор. То есть простую функцию из двух-пяти строк в лямбде не описать. Да и выравнивание тут снова мешает. То есть если в другом языке например для сортировки вполне применяли лямбду, то тут — нет


  • функциональные функции (сори за тафталогию) часто возвращают итераторы. Часто это крайне неудобно и нужны полноценные списки. Приходится оборачивать их в list и это получается громоздко и генератор выигрывает:



>>> a = [1,2,3]
>>> filter(lambda x: x > 2, a)
<filter object at 0x7f821f693b00>
>>> list(filter(lambda x: x > 2, a))
[3]
>>> [x for x in a if x > 2]
[3]

И так далее. В общем если заглядывать в реальные проекты на Python, то увидим, что функциональщина если и применяется то очень редко.

Но почему же редко. Все зависит. Как вам такое, например? Обратите внимание на конвейеры (про них в предыдущем посте)

# Множественная диспетчеризация (мультиметод)

import pandas as pd

class Data:
    uk, uk_scrbd, ru = range(3)
    
def load_data(identity):
    '''имплементация мультиметода на Python; загружает
    данные в зависимости от значения идентификатора'''
    return {
        Data.uk: lambda: do('ch01/UK2010.xls', 
                            pd.read_excel 
                           ),
        Data.ru: lambda: do('ch01/UK2010.xls', 
                            pd.read_excel, 
                            lambda o: o[o['Election Year'].notnull()]
                           )
    }[identity]()

load_data(Data.uk)

самописный конвейер может быть хорош, почему бы и нет. Хотя бы потому что он возвращает значение.


а вот map(filter плох итератором (выше я говорил)


PS: у Вас неплохие статьи, непонятно почему в минусах

Благодарю за оценку))
Дело в том, что в первых двух постах я поднял важную тему отсутствия правильного названия для ML, и что оно выбивается из общемировой парадигмы. Для хорошей эволюции требуется хорошее потрясение. Если я их потряс, то я справился), но получил ответку)))
В минусах — за оформление кода. Прошлые варианты были просто вырвиглаз. Я два дня подождал, пока читать начал, хотя поначалу пытался в IDE копировать и сам руками форматировать.
Ушло немного времени, чтобы освоиться.

Можно было просто подсказать. Это и называется отсутствием сотрудничества.
Часто это крайне неудобно и нужны полноценные списки.

Сударь, из какого языка вы пришли? Генераторы (вероятно это что, что имеется ввиду) — это ленивый подход, и он предпочтителен в ФП. Да даже в технически императивном C# подобные функции возвращают итерируемые ленивые объекты, а вовсе не окончательный массив или список. А вы хотите, чтобы за вас мапы и фильтры создавали новый список?

эм, какая разница откуда я пришёл, если именно в питоне часто нужны полноценные списки?


часто функция (в т.ч. библиотечная) принимает список, а не итератор.


а пришел я из perl


А вы хотите, чтобы за вас мапы и фильтры создавали новый список?

ага
причем если собирают ковейер из map,filter, итп, то пусть бы генерировал
а в итоге должен быть список

часто функция (в т.ч. библиотечная) принимает список, а не итератор.

Это очень странно, обычно принимают генераторы. Но если это так — то есть на то причины, и это никак не проблема map/filter/и т д!


ага
причем если собирают ковейер из map,filter, итп, то пусть бы генерировал
а в итоге должен быть список

Ни в коем случае! Список это всегда дополнительная ненужная аллокация и еще и то, что тебе кажется, что эта последовательность быстро превратится в список. Произвольный генератор на каждый следующий шаг может спутник на луну отправлять! Или вообще представлять собой бесконечную последовательность. Вот возьми к примеру


def naturals():
    i = 0
    while (True):
        i += 1
        yield i

# Вот здесь твоя идея дает бесконечный список и все умирает.
even_naturals = filter(lambda x: x % 2 == 0, naturals())

for n in even_naturals:
    print(n)
    if n > 5:
        break

Но попробуй запустить этот код в питоне, и он отработает так, как ожидается.

Но если это так — то есть на то причины, и это никак не проблема map/filter/и т д!

не проблема map/filter, но map/filter становится обязательным применять совместно с list


def naturals():

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


Вот здесь твоя идея дает бесконечный список и все умирает

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


если программируем бухгалтерию, то к списку бывает надо обратиться 2 и более раз
если программируем какие-то операции с файлами, то список файлов тоже часто бывает нужен 2 и более раз итп


поэтому в библиотеках списки файлов, хостов итп как правило принимаются диктами/листами а не итераторами

Вот в перле и программируй свою бухгалтерию, и файлы свои забери.) Мы здесь о высоких материях.)

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

next(filter) — опечатка?

Это ошибка. Материал писался 3 года назад. Все работало.
Должно быть так:
>>> filtered = iter(filter(lambda x: x % 2 == 0, seq))

А потом вот так:
>>> next(filtered)
2
>>> next(filtered)
4

Благодарю, что подметили. Текст исправлен.
Нормальное и практически полное описание «ФП на питоне» было довольно давно сделано в «междельмашевских» материалах.

Всё, что было позже на русском языке, обычно сводится к упоминанию map, filter, reduce без контекста, почему именно они выступают примером ФП.

Надо отдать должное, ваше изложение (или оригинал, если это перевод) отличается связанностью, есть преамбула о самой парадигме ФП и вы продвинулись даже до замыкания и карринга.

Но данный материал не решает основной проблемы — как и зачем этим пользоваться?
И это проблема «синтетических» примеров. Кто немного в теме, тому материал не очень интересен, и так читано перечитано. Новичок, мало чего поймёт, кроме фишек в виде list comprehension, которые он и так скорее всего знает безотносительно ФП.
Вы смотрели пост про функциональный конвейер? Он представляет собой прекрасный пример практического применения ФП на практике. При доработке он может содержать ветвления — все в функциональном стиле. Помимо этого, у меня есть примеры имплементаций моделей ML в виде конвейеров, простенькой экспертной системы и прочие вкусности типа графического интерфейса)) Хотел было продолжить серию. Будет время сделаю.

Кстати, обратите внимание на преимуществе конвейера в отладке кода — все оченнннно получается модульно.

И еще, этот материал не переводной, авторский)

Что касается list comprehension, то в английском питоновском языке еще есть dict comprehension и set comprehension, поэтому держитесь подальше от «спискового включения» ибо близоруко, т.к. dict и set по русски атрибутивно у вас не получится. Лучше говорить включение в список, в словарь, в множество

А зачем вы добавили этот и предыдущий посты в хабы F# и Clojure? Из-за одного примера конвейера на F#? Думаю, не стоит засорять хабы материалом, не имеющим к их темам отношения.

Резонно. Но темы коррелированы, а людей в ФП мало.
Python крут! Он дает кучу возможностей отстрелить себе ногу почесав ухо. Так что функциональные фичи Python нужно применять с осторожностью. Примерно на уровне регулярных выражений. Когда нет альтернативы и когда альтернатива требует кучи кода, да и пожалуй все. Работая с Python надо помнить про duck typing. Даже такой кривой итератор как в примере ниже, работает и с map, и с filter, и с reduce.
from functools import reduce
from random import randrange


class MyIterator():

    def __iter__(self):
        return self

    def __next__(self):
        result = randrange(10)
        print("Generated:", result)
        if result == 0:
            raise StopIteration()
        return result


if __name__ == '__main__':

    my_iterator = MyIterator()

    print("Map example call:", list(map(lambda x: x, my_iterator)))
    print("Filter example call:", list(filter(lambda x: x % 2 == 0, my_iterator)))
    print("Reduce example call:", reduce(lambda acc, x: acc + x, my_iterator, 0))

Правда, добавляет перчинки:
...
    seq = (1,2,3,4,5,6,7,8,9)
    mapped_static = map(lambda x: x, seq)
    print("First call of mapped static:", list(mapped_static))
    print("Second call of mapped static:", list(mapped_static))

    my_iterator = MyIterator()
    mapped_dynamic = map(lambda x: x, my_iterator)
    print("First call of mapped dynamic:", list(mapped_dynamic))
    print("Second call of mapped dynamic:", list(mapped_dynamic))

Результат будет вроде такого:
...
First call of mapped static: [1, 2, 3, 4, 5, 6, 7, 8, 9]
Second call of mapped static: []
Generated: 2
Generated: 6
Generated: 6
Generated: 9
Generated: 8
Generated: 2
Generated: 6
Generated: 5
Generated: 7
Generated: 5
Generated: 4
Generated: 4
Generated: 0
First call of mapped dynamic: [2, 6, 6, 9, 8, 2, 6, 5, 7, 5, 4, 4]
Generated: 6
Generated: 5
Generated: 1
Generated: 1
Generated: 6
Generated: 8
Generated: 0
Second call of mapped dynamic: [6, 5, 1, 1, 6, 8]

Не то чтобы ужас-ужас, но неприятно.

А уж что там на самом деле за объект в более-менее сложном проекте — это вопрос на миллион. Иногда смотришь на код — вроде обычный for item in some_list:, но присмотревшись к этому some_list понимаешь, что там внутри нифига не список. А нечто навороченное, читающее данные из очереди сообщений в дополнительных потоках, с вычиткой данных наперед и буферизацией в памяти. Ну или какой-нибудь словарь с конфигом. Присматриваешься — а там Содом с Гоморрой с получением данных из переменных окружения, локальных конфигурационных файлов и базы данных. Все это смешивается, но не взбалтывается и время от времени перечитывается. Но при этом по большей части снаружи выглядит как обычный дикт. А меньшая часть — иногда стреляет, даря незабываемые ощущения батхерта на вроде бы ровном месте.
Впрочем, проблем может доставить даже изменение типа с тапла на лист.

Так что функциональные возможности Python знать нужно, но применять — только четко понимая, что ты делаешь и к каким последствиям это приведет.
Как известно, из любой конфетки можно сделать «не то». )
Однако, пример — показателен.
Вот очень неплохая библиотека github.com/kachayev/fn.py с недостающими структурами данных и функциями, рекомендую.
Благодарю;)

Зачем этой статье метка F#, если в статье ни слова об этом? Поставил минус — это вероятно у меня единственный способ хоть как-то повлиять на спам меток в статьях у подобных хайпожоров.

На портале «хабр» у меня зарегистрирована моя страница. Вы являетесь на мою страницу и прилюдно начинаете обзываться. Так кто тут «хайпожер» тогда?

Поясняю, я указал две дополнительные метки: F# и clojure, чтобы привлечь внимание ф-программистов, в ситуации когда материалов по ФП и так мало, в надежде, что кто-то из смежных областей заинтересуется. Читайте внимательнее заголовки…

По поводу минуса. Это все претензии? А по существу темы будут? Мне поставили двойку по «матану», потому что я пришел без пиджака ;))

Спасибо, но я тщательно выбрал какие метки я хочу читать, а все остальные "я хочу привлечь из смежных областей" расцениваю как хайп. Мне не нужны другие метки и меня вполне устраивает пяток статей по теме вместо сотен ненужных мне статей. Вы не цените моё время — я ставлю минус вашей статье. Других претензий у меня нет, я не планирую даже вчитываться и терять своё время на чтение ненужных мне материалов. Пусть статью по существу оценивают участники из профильных меток, я же просто голосую ногами против спама меток. Не надо за меня решать, какие бы ещё непрофильные статьи мне воткнуть в голову и отобрать время — тут и так хватает рекламных статей, которым просто откровенно наплевать на тематичность — но если вы как программист считаете, что вам с ними по пути — ну так и получайте минусы. Если вы почитаете мои предыдущие комментарии — я вполне последователен в своём мнении, это не первый комментарий подобного рода. Не хотите? Жалко времени?


Ну так вот из-за таких как вы мне приходится после чтения заголовка делать уточнение, есть ли в тексте хоть одно упоминание F# — и если его нет, то уж извините. У меня свои принципы, я в карму не ставлю минусов, но уж если мне кажется, что кто-то планировал отнять моё время — пусть разменивается на минус. В конце концов, в предыдущей статье вы привели пример на F# — и я не стал минус ставить.


Хотите привлечь внимание из смежных меток — ну здорово! Придумывайте интересные сравнения нескольких языков, но блин, когда вообще ни одного упоминания — имейте совесть!

Самодовольство, напыщенность, надменность, высокомерие, «не читал, но осуждаю» — уникальная коллекция комплексов. Какой замечательный экземпляр для психоаналитика…

Можете продолжать навешивать ярлыки: вам факты — вы ярлык, вам идею, как улучшить статьи — вы ярлык, вам по делу — вы ярлык. Не вижу смысла продолжать разговор с человеком который развешивает на окружающих ярлык "придурки", "уроды" и подобное.

Ни разу никого не обзывал и не собираюсь. Более того, ни разу никого не унижал и тоже не собираюсь. И не следует все ставить с ног на голову. Подумайте лучше о правилах приличного поведения в научпоп сообществе, прежде чем писать комменты.
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.