21

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

Фу! Яке збочення.

Оце:

    (list(map (int.__neg__, [1,2,3,4,5])))

Правильно пишеться отак:

[-x for x in range(1, 6)]

Я вже мовчу про те що     

(list(map (lambda x:x.__neg__(), [1,2.2,3,4.4,5])))

пишеться так само.

22 Востаннє редагувалося P.Y. (18.02.2016 20:01:39)

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

bunyk написав:

Фу! Яке збочення.

Оце:

    (list(map (int.__neg__, [1,2,3,4,5])))

Правильно пишеться отак:

[-x for x in range(1, 6)]

Я взяв цей операторний метод лише для прикладу, бо він завідомо присутній у кількох вбудованих типах, що зручно для демонстрації. Але на його місці міг бути й звичайний метод.

У такому простому прикладі генераторний вираз справді зручніший, ніж map. Але якщо нам треба зробити щось складніше, послідовно пропустивши послідовність через map та filter кілька разів (що, в принципі, можна переписати генераторними виразами) і обробивши її деякими засобами itertools (які виходять за рамки можливостей for)? Змішувати обидва стилі — поганий тон, ІМНО. Крі того, вкладені for ... if ... важче читати, ніж вкладені map та filter

23

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

http://docs.hylang.org/en/latest/

Подякували: P.Y., leofun012

24

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

P.Y. написав:

послідовно пропустивши послідовність через map та filter кілька разів (що, в принципі, можна переписати генераторними виразами)

так

P.Y. написав:

і обробивши її деякими засобами itertools (які виходять за рамки можливостей for)?

То обробляти, що тут такого.

P.Y. написав:

Змішувати обидва стилі — поганий тон, ІМНО.

Я так не думаю.

P.Y. написав:

вкладені for ... if ... важче читати, ніж вкладені map та filter

Ні, тільки для лісперів. :) Ну але й вони можуть написати лямбду. Наприклад я ніколи не пишу

int.__add__

, я завжди пишу

lambda a, b: a+b

25

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

В пітоні є проблема - рядок з одного символа, і символ неможливо відрізнити за типом. :)

26

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

bunyk написав:

В пітоні є проблема - рядок з одного символа, і символ неможливо відрізнити за типом. :)

І правильно зробили, бо символ - то рядок з одного символа.

Подякували: koala1

27

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

quez написав:
bunyk написав:

В пітоні є проблема - рядок з одного символа, і символ неможливо відрізнити за типом. :)

І правильно зробили, бо символ - то рядок з одного символа.

З одного символа, який у свою чергу, складається з одного односимвольного рядка, і т.д.
Якщо ж ми хочемо уникнути цього рекурсивного визначення, пітонівський рядок можна розглядати як масив кодів символів.

28 Востаннє редагувалося P.Y. (19.02.2016 13:11:41)

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

Чим вкладені генераторні вирази гірші за map/filter?
Головний недолік — умова if, що йде в постпозиції. Тому така проста й зрозуміла конструкція:

map(f1,
filter(f2,
map(f3,
filter(f4, 
map(f5, coll)))))

перетвориться на щось ось таке:

f1(i) for i in (
  f3(i) for i in (
    f5(i) for i in coll
  ) if f4(i)
) if f2(i)

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

Інший недолік — нерозширюваність. Додаткові функції для роботи з ітераторами є в itertools, або ж їх можна написати власноруч, використовуючи приблизно той же порядок передачі параметрів. Генераторний вираз — синтаксична конструкція, зміни в яку можна додати хіба що зі зміною стандартів мови, тому його так чи інакше доведеться комбінувати з викликами ітераторних функцій. Змішаний стиль коду в одному виразі дещо погіршує читабельність.

Подякували: quez1

29

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

P.Y. написав:
quez написав:
bunyk написав:

В пітоні є проблема - рядок з одного символа, і символ неможливо відрізнити за типом. :)

І правильно зробили, бо символ - то рядок з одного символа.

З одного символа, який у свою чергу, складається з одного односимвольного рядка, і т.д.

Підловили. Символ - рядок довжиною 1.

30

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

IhorGorobets написав:

http://docs.hylang.org/en/latest/

Виглядає як дуже урізана Clojure-подібна мова з інтеграцією в Python. Також має сенс подивитись https://github.com/pixie-lang/pixie — ще одна реалізація схожої ідеї, судячи з опису, більш повно реалізована (зокрема, є цикл loop-recur, відсутній у hy), хоча й більш віддалена від власне пітона.

Але ідея, яку я тут намагаюсь просувати — особливої потреби в «ліспі для пітона» нема, бо Python початково є майже-ліспом (без багатьох типово ліспівських особливостей, таких як S-вирази чи макроси — проте, на ньому можна писати код у стилі ФП, а відсутні фічі замінити засобами самого пітона без написання свого транслятора).

31

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

P.Y. написав:

P. S. Мені більше заважає протилежна проблема: методи не можна використовувати як аналоги функцій, не прив'язуючись до конкретного класу й обходячись без враперів чи лямбд. Тобто, ось так можна:

(list(map (int.__neg__, [1,2,3,4,5])))

і так теж:

(list(map (float.__neg__, [1.1,2.2,3.3,4.4,5.5])))

а для різнотипних даних це не працює (і де ваш хвалений дактайп, коли він був би так корисний?), треба вже робити так:

(list(map (lambda x:x.__neg__(), [1,2.2,3,4.4,5])))

а хотілось би так:

(list(map (.__neg__, [1,2.2,3,4.4,5])))

Черговий костиль:

class DT:
    def __getattr__(self, name):return eval(
        'lambda x, *args, **kwargs:x.'+name+'(*args, **kwargs)')
dt=DT()

Маючи його, наведений вище приклад можна зробити ось так:

(list(map (dt.__neg__, [1,2.2,3,4.4,5])))

32 Востаннє редагувалося ADR (02.03.2016 21:32:10)

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

.

33

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

Якщо вже мова зайшла за Clojure та його нащадків, поговорімо про таку корисну річ, як «->>» — макрос для розгортання вкладених виразів.

Для чого це взагалі потрібно? Працюючи з серією вкладених викликів спискових/ітераторних функцій (таких як map, filter, reduce та ін.), ми неминуче зіткнемося з характерною ліспівською батареєю дужок наприкінці виразу. І хоча сучасні редактори та середовища розробки дозволяють швидко знайти парну дужку, читати такі вирази очима доволі незручно, та й ризик помилки зростає. І якщо в Common Lisp чи Scheme з цим якось миряться (бо як же інакше?), а в Python просто міняють функціональний стиль на процедурний, то в Clojure можна цього уникнути:

(->> lst
 (map f1)
 (filter f2)
 (map f3)
 (map f4)
 (reduce f5)
)

що після обробки макросів трансформується в

(reduce f5 (map f4 (map f3 (filter f2 (map f1 lst)))))

тобто, кожен вираз зі списку підставляється в наступний вираз останнім аргументом.

Тепер повернімось до пітона. В принципі, зовсім необов'язково пакувати все в один вираз — його можна переписати як серію присвоєнь:

x=lst
x=map(f1, x)
x=filter(f2, x)
x=map(f3, x)
x=map(f4, x)
x=reduce(f5, x)

Хоча любителям функціонального стилю може сподобатися така функція:

def rarg(x): 
    return lambda *args, **kwargs:\
        rarg(args[0](*args[1:]+(x,), **kwargs)) if args else x

Маючи її, наведений вище приклад можна переписати так:

(rarg(lst)
 (map, f1)
 (filter, f2)
 (map, f3)
 (map, f4)
 (reduce, f5)
())

Або більш зручний для демонстрації приклад:

>>> rarg('qwertyuiop')(filter, lambda c:c>'q')(map, repr)(', '.join)()
"'w', 'r', 't', 'y', 'u'"

34 Востаннє редагувалося P.Y. (08.03.2016 23:39:18)

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

Проблема:
raise неможливо використати всередині виразу (а отже, кинути виключення безпосередньо з лямбди).
Костилі:
а)

def throw(e):
    raise e

б)

throw=(___ for ___ in ()).throw 

В обох випадках, функцію throw можна використати в виразі, передавши їй виключення як аргумент. Або ж можна не визначати її наперед, а брати як метод генераторного виразу з правої частини прикладу (б)

35

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

Перестань писати лямбди, це тобі не лісп.

Подякували: koala, ADR2

36

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

Ідея в тому, що Python — це також і LISP (чи, принаймні, мова з більш вираженою ліспоїдністю, ніж, скажімо, Java). Лямбди, до речі, є загальною тенденцією в більшості мов, що активно розвиваються в наш час.

37 Востаннє редагувалося P.Y. (31.03.2016 14:15:39)

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

Хвостова рекурсія (тобто, кінцевий виклик функції з функції, при якому дані попередньої функції вивільняються зі стеку, що дозволяє здійснювати нескінченну рекурсію без переповнення). Це основний засіб організації циклів у Scheme. Python не має вбудованих засобів для хвостової оптимізації. Так само, ці засоби відсутні в JVM, що не завадило Clojure їх реалізувати.

Clojure має два варіанти організації хвостової рекурсії: loop...recur i trampoline. Перший на апаратному рівні реалізується як цикл і є синтаксичним аналогом іменованого let у Scheme. Другий являє собою доволі простий костиль, який, проте, дозволяє робити оптимізовані рекурсивні виклики між кількома різними функціями.

Реалізується trampoline елементарно:

def trampoline(f):
    while callable(f):f=f()
    return f

Щоб перевірити, що це справді працює, можна запустити простеньку функцію:

def cnt(n):
    print(n)
    return (lambda:cnt(n-1)) if n else 0

trampoline(cnt(1000000))

Ми побачимо, як на екран буде виведено числа від 1000000 до 0, при цьому кількість пам'яті, яку споживає python, помітно не збільшиться. Для порівняння, якщо зробити так:

def cnt1(n):
    print(n)
    return (cnt1(n-1)) if n else 0

cnt1(1000000)

ми отримаємо екран, заповнений повідомленнями про помилку через досягнення максимальної глибини рекурсії.

38

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

мені не вистачає нормального циклу з флоатом, на днях хтів протабулювати функцію з кроком 0.1, так нічо і не вийшло

39 Востаннє редагувалося P.Y. (31.03.2016 15:01:36)

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

FakiNyan написав:

мені не вистачає нормального циклу з флоатом, на днях хтів протабулювати функцію з кроком 0.1, так нічо і не вийшло

Можна спробувати скористатися моїм попереднім костилем

for i in cfor(0, lambda x: x<10, lambda x: x+0.1):
    print(i)

Хоча простіше зробити так:

for i in map(lambda x: x*0.1, range(100)):
    print(i)

Оскільки операції з float'ами дають первну похибку, результати цих двох реалізацій можуть відрізнятись (бо в першому випадку float додається до float при кожному проходженні, у другому для рахунку використовується int, який потім щоразу множиться на float).

Варіант із цілим кроком має давати меншу похибку; якщо ж треба зробити цикл для довільного діапазону, я б зробив це так:

minval=1234
maxval=1342
step=0.1
for i in map(lambda x: x*step, range(int(minval/step), int(maxval/step))):
    print(i)

40

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

while, oh while!