Подтвердить что ты не робот

Использование OR в качестве управления ветвью в FP

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

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

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

def g():
    # something else.

f() or g()

Затем, если f() оценивает какое-либо истинное условие, тогда возвращается это значение, если нет, g() оценивается и возвращается любое значение, которое оно возвращает, true или false. Это дает нам возможность реализовать оператор 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.

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

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

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

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

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

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

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

4b9b3361

Ответ 1

У ваших интервьюеров, должно быть, был "функциональный фон". Раньше обычно было написано

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

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

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

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

Ответ 2

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

Ответ 3

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

Ответ 4

Как сказал Эли; Кроме того, выполнение потока управления чисто логическими операторами, как правило, преподается во вводных классах 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

Ответ 5

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

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

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

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

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

(В стороне: я нахожу, что построители Lisp не совсем соответствуют тем, что их имена находятся на других языках, что может сбить с толку. Lisp IF похож на C-тернарный оператор ?: или Python условные выражения, например, не их if-statements. Аналогично, 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. Означает ли это, что "контроль потока" или "функциональный"? Я думаю, это зависит от ваших определений.

Ответ 6

  • Он может считаться "функциональным" в смысле стиля программирования, который является/был предпочтительным в функциональном языке. В этом нет ничего функционального.

  • Это просто синтаксис.

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

    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 [], но это довольно сложно.