Использование OR в качестве контроля ветвления в FP - PullRequest
19 голосов
/ 29 марта 2011

На прошлой неделе я взял интервью, в ходе которого я узнал несколько вещей о питоне, о которых я не знал (или точнее понял, как их можно использовать), в первую очередь, и содержание этого вопроса - использование orдля целей отраслевого контроля.

Так, например, если мы запустим:

def f():
    # do something. I'd use ... but that's actually a python object.

def g():
    # something else.

f() or g()

Затем, если f() оценивает какое-либо истинное условие, возвращается это значение, если нет, g() оценивается и всеВозвращаемое значение возвращается, истина или ложь.Это дает нам возможность реализовать оператор if с использованием or ключевых слов.

Мы также можем использовать and, так что f() and g() вернет значение g(), если f() истиннои значение f(), если g() равно false.

Мне сказали, что это (использование or для управления ветвлением) является распространенным явлением в таких языках, как lisp (отсюда и тег lisp).В настоящее время я следую SICP Схеме обучения, поэтому я вижу, что (or (f x) (g x)) вернет значение (g x) при условии, что (f x) равно #f.

Я в замешательствеотносительно того, есть ли какое-либо преимущество этой техники.Ясно, что он обеспечивает управление ветвями, но мне кажется, что встроенные ключевые слова более понятны.

Я также не уверен, является ли это "функциональным" или нет?Мое понимание чистого функционального программирования заключается в том, что вы используете конструкции, подобные этой (пример из моих недавних экспериментов с эрлангом):

makeeven(N,1) -> N+1;
makeeven(N,0) -> N;
makeeven(N)   -> makeeven(N,N rem 2). 

Или более лучший, более сложный пример использования шаблонного метапрограммирования в C ++ (обнаружено через cpp-next.com).Мой мыслительный процесс состоит в том, что один аспект функционального программирования сводится к использованию кусочно определенных функций в коде для управления ветвями (и, если вы можете управлять им, хвостовой рекурсией).

Итак, мойвопросы:

  • Это "функционал"?Похоже, мои интервьюеры сказали, что у них есть опыт функционального программирования, но это не соответствовало тому, что я считал функциональным.Я не вижу причин, по которым у вас не может быть логического оператора как части функции - он, похоже, прекрасно подходит для концепции функций более высокого порядка.Я просто не думал, что использование логических операторов - это то, как функциональные программисты достигают управления ветвями.Правильно?Неправильно?Я вижу, что схемы используют логические вентили для управления ветвлением, поэтому я думаю, что это похожая (связанная) концепция?
  • Есть ли какое-то преимущество в использовании этой техники?Это просто языковая лаконичность / синтаксическая проблема, или есть какие-то последствия с точки зрения построения интерпретатора для использования этой конструкции?
  • Есть ли варианты использования для этой техники?Или это не очень часто используется?Это вообще используется?Как парень-самоучка, я никогда не видел его раньше, хотя это само по себе не обязательно удивительно.

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

Ответы [ 6 ]

12 голосов
/ 29 марта 2011

Ваши интервьюеры должны были иметь "функциональный опыт" в обратном направлении. Обычно пишется

(or (some-condition) (some-side-effect))

но в CL и в реализации Scheme, которая его поддерживает, гораздо лучше писать с unless. То же самое касается and против when.

Итак, чтобы быть более конкретным - это не более функционально (и фактически общее использование этих вещей было для односторонних условных выражений, которые не функциональны с самого начала); нет никакого преимущества (что становится очень очевидным в этих языках, когда вы знаете, что все равно реализованы как макросы - например, большинство реализаций or и and расширяются до if); и любые возможные варианты использования должны использовать when и unless, если они есть в вашей реализации, иначе лучше определить их как макросы, чем не использовать их.

О, и вы могли бы использовать их комбинацию вместо двухстороннего if, но это было бы ужасно уродливо.

6 голосов
/ 29 марта 2011

Я не знаю каких-либо проблем с тем, как этот код будет выполняться, но его непонятно читать для непосвященных. На самом деле, этот вид синтаксиса похож на анти-паттерн Python: вы можете сделать это, но это никак не Pythonic.

5 голосов
/ 29 марта 2011

condition and true_branch or false_branch работает на всех языках, которые имеют короткие логические операторы.С другой стороны, не очень хорошая идея использовать язык, в котором значения имеют логическое значение.

Например

zero = (1==0) and 0 or 1   # (1==0) -> False 
zero = (False and 0) or 1  # (False and X) -> X
zero = 0 or 1              # 0 is False in most languages
zero = False or 1 
zero = 1
2 голосов
/ 30 марта 2011

Вы упомянули только тот случай, когда нужно вычислить ровно 2 выражения.Что произойдет, если их будет 5?

;; returns first true value, evaluating only as many as needed
(or (f x) (g x) (h x) (i x) (j x))

Будете ли вы вкладывать операторы if?Я не уверен, как бы я сделал это в Python.Это почти примерно так:

any(c(x) for c in [f, g, h, i, j])

за исключением того, что Python any выбрасывает значение и просто возвращает True.(Возможно, есть способ сделать это с itertools.dropwhile, но мне это кажется немного неловким. Или, может быть, я просто упускаю очевидный способ.)

(В качестве отступления: я считаю, что ЛиспВстроенные функции не вполне соответствуют тому, что их имена в других языках, что может сбивать с толку. Lisp IF подобен тернарному оператору C ?: или условным выражениям Python, например, а не их if-Аналогично, Lisp OR в некотором смысле больше похож (но не совсем похож) на Python any(), который принимает только 2 выражения. Поскольку обычный IF уже возвращает значение, нет смысла иметь отдельный вид«если», которое нельзя использовать подобным образом, или отдельный вид «или», который принимает только два значения. Это уже столь же гибко, как и менее распространенный вариант в других языках.)

У меня случаетсяпишите код, подобный этому, прямо сейчас, по совпадению, где некоторые из функций - «спросите у сервера ответ», и я хочу остановиться, как только получу положительный ответ.Я бы никогда не использовал OR там, где я действительно хотел бы сказать IF, но я бы скорее сказал:

(setq did-we-pass (or (try-this x)
                      (try-that x)
                      (try-some-other-thing x)
                      (heck-maybe-this-will-work x))

, чем создавал бы большое дерево IF.Это квалифицируется как «управление потоком» или «функционал»?Я думаю, это зависит от ваших определений.

2 голосов
/ 29 марта 2011

Как сказал Илай;Кроме того, выполнение потока управления исключительно с помощью логических операторов, как правило, преподается во вводных классах FP - скорее, как упражнение на ум, а не то, что вы обязательно хотите использовать IRL.Всегда хорошо иметь возможность переводить любой оператор управления до if.

Теперь, большая разница между FP и другими языками заключается в том, что в более функциональных языках if на самом деле является выражением, а незаявление.if блок всегда имеет значение!Семейство языков C имеет макро-версию этого - конструкция test? consequent : alternative - но она становится действительно нечитаемой, если вы вкладываете больше выражений.

До Python 2.5, если вы хотите иметь элемент управленияВыражение потока в Python, возможно, вам придется использовать логические операторы.Однако в Python 2.5 является синтаксисом if-выражения, подобным FP, поэтому вы можете сделать что-то вроде этого:

(42 if True else 7) + 35

См. PEP 308

0 голосов
/ 29 марта 2011
  1. Его можно считать «функциональным» в смысле стиля программирования, который был / был предпочтен в функциональном языке.В противном случае в этом нет ничего функционального.

  2. Это всего лишь синтаксис.

  3. Иногда это может быть более удобочитаемым или, например:

    def foo(bar=None):
        bar = bar or []
        ...
        return bar
    
    def baz(elems):
        print "You have %s elements." % (len(elems) or "no")
    

Вы можете использовать bar if bar else [], но это довольно сложно.

...