Фиксирующая комбинаторика переопределение элемента
Мой код опирается на версию Element
, которая работает как MemberQ
, но когда я загружаю Combinatorica
, Element
получает переопределение для работы типа Part
. Какой самый простой способ устранить этот конфликт? В частности, каков синтаксис для удаления определения Combinatorica из DownValues
? Вот что я получаю за DownValues[Element]
{HoldPattern[
Combinatorica`Private`a_List [Element]
{Combinatorica`Private`index___}] :>
Combinatorica`Private`a[[Combinatorica`Private`index]],
HoldPattern[Private`x_ [Element] Private`list_List] :>
MemberQ[Private`list, Private`x]}
4 ответа:
Если ваша цель состоит в том, чтобы предотвратить установку определения Combinatorica в первую очередь, вы можете достичь этого результата, загрузив пакет в первый раз таким образом:
Однако это почти наверняка приведет к тому, что любые функции Combinatorica, зависящие от определения, потерпят неудачу (что может иметь или не иметь значение в вашем конкретном приложении).Block[{Element}, Needs["Combinatorica`"]]
Вы можете сделать несколько вещей. Введем функцию удобства
ClearAll[redef]; SetAttributes[redef, HoldRest]; redef[f_, code_] := (Unprotect[f]; code; Protect[f])
Если вы уверены в порядке определений, вы можете сделать что-то вроде
redef[Element, DownValues[Element] = Rest[DownValues[Element]]]
Если вы хотите удалить определения, основанные на контексте, вы можете сделать что-то вроде этого:
redef[Element, DownValues[Element] = DeleteCases[DownValues[Element], rule_ /; Cases[rule, x_Symbol /; (StringSplit[Context[x], "`"][[1]] === "Combinatorica"), Infinity, Heads -> True] =!= {}]]
Можно также использовать более мягкий способ-изменить порядок определений, а не удалять:
Существует много других способов решения этой проблемы. Еще один (который я уже рекомендовал) - использовать UpValues, если это подходит. Последнее, о чем я хочу упомянуть здесь, - это создание своего рода пользовательской конструкции динамической области, основанной на блоке, и обертывание ее вокруг вашего кода. Лично я нахожу его самым безопасным вариантом, если вы хотите, чтобы применялось строго ваше определение (потому что его не волнует порядок, в котором могли бы быть созданы различные определения - он удаляет их все и добавляет только ваше). Это также безопаснее в том, что вне тех мест, где вы хотите, чтобы ваши определения применялись (по "места" я имею в виду части оценочного стека), другие определения все равно будут применяться, так что это, кажется, наименее навязчивый способ. Вот как это может выглядеть:redef[Element, DownValues[Element] = RotateRight[DownValues[Element]]]
elementDef[] := Element[x_, list_List] := MemberQ[list, x]; ClearAll[elemExec]; SetAttributes[elemExec, HoldAll]; elemExec[code_] := Block[{Element}, elementDef[]; code];
Пример использования:
In[10]:= elemExec[Element[1,{1,2,3}]] Out[10]= True
Правка:
Если вам нужно автоматизировать использование блока, вот пример пакета, чтобы показать один из способов, как это можно сделать:
BeginPackage["Test`"] var; f1; f2; Begin["`Private`"]; (* Implementations of your functions *) var = 1; f1[x_, y_List] := If[Element[x, y], x^2]; f2[x_, y_List] := If[Element[x, y], x^3]; elementDef[] := Element[x_, list_List] := MemberQ[list, x]; (* The following part of the package is defined at the start and you don't touch it any more, when adding new functions to the package *) mainContext = StringReplace[Context[], x__ ~~ "Private`" :> x]; SetAttributes[elemExec, HoldAll]; elemExec[code_] := Block[{Element}, elementDef[]; code]; postprocessDefs[context_String] := Map[ ToExpression[#, StandardForm, Function[sym,DownValues[sym] = DownValues[sym] /. Verbatim[RuleDelayed][lhs_,rhs_] :> (lhs :> elemExec[rhs])]] &, Select[Names[context <> "*"], ToExpression[#, StandardForm, DownValues] =!= {} &]]; postprocessDefs[mainContext]; End[] EndPackage[]
Вы можете загрузить пакет и посмотреть значения для F1 и f2, например:
In[17]:= DownValues[f1] Out[17]= {HoldPattern[f1[Test`Private`x_,Test`Private`y_List]]:> Test`Private`elemExec[If[Test`Private`x\[Element]Test`Private`y,Test`Private`x^2]]}
Та же схема будет работать и для функции не в одном пакете. На самом деле, вы могли бы отделиться нижняя часть (пакет обработки кода) должна быть пакетом сама по себе, импортируйте ее в любую другую пакет, в котором вы хотите ввести блок в определения ваших функций, а затем просто вызвать что-то вроде
postprocessDefs[mainContext]
, как описано выше. Вы можете сделать функцию, которая делает определения внутри блока (elementDef
здесь), дополнительным параметром к обобщенной версииelemExec
, что сделает этот подход более модульным и многоразовым.Если вы хотите быть более избирательными в отношении функций, в которые вы хотите ввести блок, это также может быть сделано различными способами. На самом деле, вся схема блок-инжекции может быть сделана чище, но она потребует немного больше внимания при реализации каждой функции, в то время как вышеописанный подход полностью автоматизирован. Я могу опубликовать код, который проиллюстрирует это, если потребуется.
Еще одна вещь: за менее навязчивый характер этого метода вы платите определенную цену-динамический объем (блок) обычно составляет труднее контролировать, чем лексически ограниченные конструкции. Таким образом, вы должны точно знать, какие части стека оценки вы хотите применить. Например, я бы не решился вводить блок в определение функции более высокого порядка, которая принимает некоторые функции в качестве параметров, поскольку эти функции могут исходить из кода, который принимает другие определения (например, функции Combinatorica, полагающиеся на перегруженный элемент). Это не большая проблема, просто требует осторожности.
Нижняя строка из этого, кажется, следует: старайтесь избегать перегрузки встроенных модулей, если это вообще возможно. В этом случае вы столкнулись с этим конфликтом определений сами, но было бы еще хуже, если бы тот, кто сталкивается с этой проблемой, был пользователем вашего пакета (может быть, вы сами через несколько месяцев), который хочет объединить ваш пакет с другим (который перегружает те же системные функции, что и ваш). Конечно, это также зависит от того, кто будет пользователями вашего пакета - только вы сами или потенциально другие. Но с точки зрения проектируйте, и в долгосрочной перспективе, возможно, Вам будет лучше исходить из последнего сценария с самого начала.
Чтобы удалить определение
Combinatorica
, ИспользуйтеUnset
или эквивалентная форма=.
. Шаблон для снятия установки вы можете взять из выводаInformation
, который вы показываете в вопросе:Unprotect[Element]; Element[a_List, {index___}] =. Protect[Element];
Беспокойство, конечно, будет заключаться в том, что
Combinatorica
внутренне зависит от этого непродуманного переопределения, но у вас есть основания полагать, что это не так, поскольку выводInformation
из переопределенногоElement
говорит:Использование функции Элемент в комбинаторике теперь есть устарел, хотя ... вызов функции Элемент[a, p] все еще дает pth элемент вложенного списка a, где p-a список индексов.
HTH
Я предлагаю совершенно иной подход, чем удаление элемента из нижних значений. Просто используйте полное имя функции элемента.
Итак, если оригинал
System`Element[]
По умолчанию теперь
Combinatorica`Element[]
Из-за загрузки пакета Combinatorica.
Просто явно используйте
System`Element[]
Там, где вам это нужно. Конечно, проверьте, что система является правильным контекстом, используя функцию контекста:
Context[Element]
Этот подход обеспечивает несколько вещей:
- пакет Combinatorica по-прежнему будет работать в вашем ноутбуке, даже если пакет Combinatorica будет обновлен в будущем
- вам не придется переопределять функцию элемента, как некоторые предположили
- Вы можете использовать функцию Combinatorica ' Element, когда это необходимо
Единственным недостатком является необходимость явно писать его каждый раз.