Модуль Mathematica по сравнению с блоком или блоком - руководство, практическое правило использования?

Леонид писал в четвертой главе своей книги: ...Модуль, Блок и С. Эти конструкции подробно описаны в Mathematica Book и Mathematica Help, поэтому здесь я скажу о них всего несколько слов. ...

Из того, что я прочитал (смог найти), я все еще в неведении. Для упакованных функций я (просто) использую модуль, потому что он работает, и я знаю конструкцию. Хотя, возможно, это не лучший выбор. Мне не совсем понятно (из документации), когда, где и зачем использовать With (или Block).

Вопрос. Есть ли эмпирическое правило/рекомендация о том, когда использовать Module, With или Block (для функций в пакетах)? Есть ли ограничения по сравнению с модулем? Документы говорят, что With быстрее. Я хочу иметь возможность защищать свой =выбор= для модуля (или другой конструкции).


person nilo de roock    schedule 12.07.2011    source источник
comment
Аналогичный вопрос по MM.SE: Каковы варианты использования для разных конструкций области видимости?   -  person E.P.    schedule 18.02.2015


Ответы (4)


Более практическую разницу между Block и Module можно увидеть здесь:

Module[{x}, x]
Block[{x}, x]
(*
-> x$1979
   x
*)

Поэтому, если вы хотите вернуть, например, x, вы можете использовать Block. Например,

Plot[D[Sin[x], x], {x, 0, 10}]

не работает; чтобы заставить его работать, можно было бы использовать

Plot[Block[{x}, D[Sin[x], x]], {x, 0, 10}]

(конечно, это не идеально, это просто пример).

Другое использование - это что-то вроде Block[{$RecursionLimit = 1000},...], которое временно заменяет $RecursionLimit (Module не сработало бы, поскольку оно переименовывает $RecursionLimit).

Можно также использовать Block для блокировки оценки чего-либо, например

Block[{Sin}, Sin[.5]] // Trace
(*
-> {Block[{Sin},Sin[0.5]],Sin[0.5],0.479426}
*)

т. е. он возвращает Sin[0.5], который оценивается только после завершения выполнения Block. Это потому, что Sin внутри Block — это просто символ, а не функция синуса. Вы могли бы даже сделать что-то вроде

Block[{Sin = Cos[#/4] &}, Sin[Pi]]
(*
-> 1/Sqrt[2]
*)

(используйте Trace, чтобы увидеть, как это работает). Таким образом, вы также можете использовать Block для локального переопределения встроенных функций:

Block[{Plus = Times}, 3 + 2]
(*
-> 6
*)
person acl    schedule 12.07.2011
comment
Следуя вашему примеру, я попробовал это. Очистить[х]; х = 1; Module[{x}, x] — он вернулся с неожиданным (для меня) результатом x$114. - person nilo de roock; 12.07.2011
comment
Пример с $RecursionLimit очень полезен. - person nilo de roock; 12.07.2011
comment
Что касается вашего примера с Plot. Я считаю, что у Plot есть некоторые эвристики, чтобы решить, оценивать ли его аргумент до числового значения, замененного на x, или только после. Единственная разница в том, что вы используете Block, меняется, когда Plot будет оценивать свой аргумент. Это не иллюстрирует, как работает Block. Вы можете проверить это, включив оператор Print[x] в аргумент Plot. (Установка недокументированной опции Evaluated на False в Plot не работает.) - person Szabolcs; 12.07.2011
comment
Я хочу сказать, что проблема с этим Plot заключается в оценке аргумента. Правильный способ заставить это работать - Plot[Evaluate[...], ...]. Тот факт, что обертывание аргумента в Block также исправляет его, является случайным и происходит из-за внутренней эвристики Plot, определяющей порядок оценки. - person Szabolcs; 12.07.2011
comment
@Szabolcs Хорошо, может быть. Я не оспариваю бит Evaluate, поэтому я сказал, что он не идеален в том виде, в котором я его здесь показываю. - person acl; 12.07.2011
comment
@Szabolcs, поэтому я попробовал Plot[Block[{x}, Sow[x]; D[Sin[x], x]], {x, 0, 10}] // Reap, и Block каждый раз оценивается. Итак, вы хотите сказать, что тот факт, что оценка не происходит, не связан с Block? Я согласен, но все, что я пытался продемонстрировать, это то, что тот факт, что Block не переименовывает x, можно использовать так, как я показал (для случаев, когда вы не можете просто Evaluate; здесь это перебор). Или я пропустил вашу мысль? - person acl; 12.07.2011
comment
@acl, я был не совсем прав в своих предположениях, и теперь немного запутался. Я предполагаю, что если Plot установить значение x до оценки его аргумента, то Block не поможет, так как мы получим что-то вроде Block[{1}, D[Sin[1], 1]]. Здесь должны быть какие-то странности с правилами области видимости... Мне совершенно не ясно, почему именно такое использование Block предотвращает ошибки. - person Szabolcs; 12.07.2011
comment
@ ndroock1 что делает это неожиданным? Идея состоит в том, что Module переименовывает свои аргументы, чтобы на них не влияли определения вне тела Module. - person acl; 12.07.2011
comment
@acl. Ты прав. Я запутался сам. Я думал, что он коснулся x, но это не так. Теперь многое стало понятнее. - person nilo de roock; 12.07.2011

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

Module[{x}, ...] является самым безопасным и может понадобиться, если либо

  1. Существуют существующие определения для x, которые вы не хотите нарушать во время оценки модуля, или

  2. Существует код, в котором x не определен (например, такой код, как Integrate[..., x]).

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

Если вы уверены, что нет важных существующих определений для x или любого кода, полагающегося на то, что он не определен, то Block[{x}, ...] часто работает быстрее. (Обратите внимание, что в проекте, полностью написанном вами, уверенность в этих условиях является разумным стандартом «инкапсуляции», который вы, возможно, захотите применить в любом случае, и поэтому Блок часто является разумным выбором в таких ситуациях.)

With[{x = ...}, expr] — единственная конструкция области видимости, которая вводит значение x внутрь Hold[...]. Это полезно и важно. With может быть быстрее или медленнее, чем Block, в зависимости от expr и конкретного выбранного пути оценки. Однако With менее гибок, так как вы не можете изменить определение x внутри expr.

person Andrew Moylan    schedule 12.07.2011
comment
+1 Хорошее резюме! У меня есть только два комментария: во-первых, если мы согласимся с тем, что Rule и RuleDelayed являются конструкциями области видимости (которыми они в некотором смысле и являются), они дают другой (неэквивалентный) способ внедрения внутри удерживаемых выражений. Во-вторых, я бы не использовал Block для простой инкапсуляции, за исключением случаев, когда его динамическая функциональность области видимости полностью необходима - трудно отвечать за некоторые части стека выполнения, особенно если вы передаете функции в качестве параметров, где произвольные код может быть выполнен. Небольшие улучшения скорости от использования Block сейчас могут привести к очень тонким ошибкам в будущем. - person Leonid Shifrin; 13.07.2011
comment
Это хороший момент в том, что инкапсуляция с Block небезопасна при обработке пользовательских функций или выражений, поскольку они могут ссылаться на глобальные переменные. В этом случае Block может быть безопасным только в том случае, если он находится в закрытом разделе пакета, то есть после оператора Begin["`Private`"]. - person Andrew Moylan; 14.07.2011
comment
Разве Unique[] не является другим способом создания и возврата нового символа? Module[{x},x] производит что-то вроде $x123, тогда как Unique[] производит что-то вроде $234. - person Reb.Cabin; 15.12.2011
comment
Да Unique[] также можно использовать для создания нового символа. Преимущество Module[{x}...] над чем-то вроде x=Unique[] заключается в том, что вы получите уникальный x, используемый везде, например. Установите такие операторы, как x=5. Такой оператор не будет работать после x=Unique[]. Вам понадобится гимнастика, такая как Evaluate[x]=5. - person Andrew Moylan; 16.12.2011

Андрей уже дал очень исчерпывающий ответ. Подводя итог, отметим, что Module предназначен для определения локальных переменных, которые можно переопределить в рамках определения функции, а With — для определения локальных констант, которые могут не быть. Вы также не можете определить локальную константу на основе определения другой локальной константы, которую вы установили в том же операторе With, или иметь несколько символов в левой части определения. То есть не работает следующее.

With[{{a,b}= OptionValue /@ {opt1,opt2} }, ...]

Я обычно устанавливаю сложные определения функций с Module, заключающим With. Я установил все локальные константы, которые я могу сначала внутри With, например. Length данных, переданных функции, если мне это нужно, а затем другие локальные переменные по мере необходимости. Причина в том, что With немного быстрее, потому что у вас действительно есть константы, а не переменные.

person Verbeia    schedule 12.07.2011
comment
На самом деле (по крайней мере, в v7) вы также не можете определить локальные переменные в терминах других переменных в Module, по крайней мере, на этапе определения, т.е. Module[{a = 1, b = a}, b] возвращает a, а Block[{a = 1, b = a}, b] возвращает 1. Поскольку переменные неизменяемы в With, это никак не обойти, но в теле Module вы можете установить b = a и заставить его вести себя как Block. - person rcollyer; 12.07.2011
comment
Я склонен использовать With в тех местах, где я должен использовать Function, чтобы указать имена переменных. Я делаю это, чтобы чистые функции вели себя немного лучше, но Function, вероятно, более подходит. - person rcollyer; 12.07.2011
comment
@rcollyer: да, я имел в виду, что затем я определяю локальную переменную, которая зависит от другой, внутри тела Module и внутри заключенного With. Извините, если это было непонятно. Я использовал именованные константы в With, если я собираюсь использовать их более одного раза в определении функции. В противном случае следует использовать чистые функции. - person Verbeia; 12.07.2011

Я хотел бы отметить, что официальная документация о разнице между Block и Module доступна по адресу http://reference.wolfram.com/mathematica/tutorial/BlocksComparedWithModules.html.

person Meng Lu    schedule 13.07.2011
comment
Спасибо. - Я думаю, что вы проиндексировали свои файлы лучше, чем я. По большей части я завишу от Google. - person nilo de roock; 13.07.2011