6
$\begingroup$

I'd like to use for MemberQ, but I don't want to break anything! The usage would be like this:

test = {1, 2, 3, 4}
find = {3, 4};
Select[test, (# ∈ find) &]

I know how to do this with the notation package, but I'm worried that it might cause problems, has anyone already done this?

$\endgroup$

3 Answers 3

12
$\begingroup$

The big issue here is that is a system defined symbol and messing with it in this way can have all manner of unintended consequences. You don't know what is using it behind the scenes.

If you really need to then you can use Infix notion on your own in function.

in[form_, list_] := MemberQ[list, form]

Select[test, #~in~find &]
(* {3, 4} *)

Or you can use the build in functions like MemberQ, ContainsAny, Subset, and so on.

Hope this helps.

$\endgroup$
9
$\begingroup$

To avoid any conflict with the built-in symbol for \[Element], I would use the small element symbol, instead (this is a different unicode character from ). Here is a way to define its use as an infix operator by way of a template:

ClearAll[myMemberQ]
appearanceIn[x_, y_] := 
 TemplateBox[{x, y}, "myMemberQ", 
  DisplayFunction :> (RowBox[{#1, "∊", #2}] &), 
  InterpretationFunction :> (RowBox[{"myMemberQ", "[", 
     RowBox[{#2, ",", #1}], "]"}] &)]

myMemberQ[l_List, e_] := MemberQ[l, e]

myMemberQ /: MakeBoxes[myMemberQ[l_, e_], StandardForm] := 
 appearanceIn[ToBoxes[e], ToBoxes[l]]

SetOptions[EvaluationNotebook[], 
 InputAliases -> 
  DeleteDuplicates@
   Join[{"in" -> appearanceIn["\[Placeholder]", "\[Placeholder]"]}, 
    InputAliases /. 
      Quiet[Options[EvaluationNotebook[], InputAliases]] /. 
     InputAliases -> {}]]

This defines a function myMemberQ that returns the same as MemberQ if the first argument is a list. If not, it defaults to a display that shows the arguments with the infix symbol between them. This display is defined in appearanceIn. The same appearance function is also used to make the input appear in the form x ∊ Y. To achieve that, I add an input alias that you invoke by entering escinesc. Upon typing this, the template for the infix notation automatically appears.

With this, you can enter things like this:

2 ∊ {1, 2, 3}

(* ==> True *)

statement = x ∊ set

(* ==> x ∊ set *)

Above, the symbolic expression on the last line remains unevaluated because set isn't a List.

But you can work with the unevaluated expression as usual:

statement /. set -> {x, y, z}

(* ==> True *)

Likewise, the example in the question works as follows:

test = {1, 2, 3, 4}
find = {3, 4};
Select[test, (# ∊ find) &]

(* ==> {1, 2, 3, 4} *)

(* ==> {3, 4} *)

Note: Wherever the symbol appears in the code examples above, I entered it by typing escinesc. So you can't copy and paste the code cells shown here. You have to use the input alias.

However, you can copy and paste forms like x ∊ set within the notebook, and they will retain their functionality (i.e., they can later be evaluated).

$\endgroup$
3
$\begingroup$

The Notation package comes into its own for those areas requiring specialized 2D formatting within a notebook. For function's involving common usage however, such as the OP's variation on MemberQ, a custom function is likely to cause less issues in the long run (such functions are also more likely to be loaded within an init.m file which is inadvisable working with the Notation package).

So if it is to be a custom function, the next task is what to call the thing. This is no small matter. There are many competing tensions; clarity, brevity, naturalness, efficiency, consistency, generality, cadence, extendability and on it goes. Should it be completely new or else a variation on an existing System function or not implemented at all? Considered decisions here can pay off in the long run.

In the case at hand, you have provided a pure function however there are advantages to creating an operator form since a network effect arises from combining different operator forms (particularly in Data Science applications). Some possibilities:

Intersection[test, find]
(* Actually do nothing and stick to inbuilt functions,
likely to be fast, keeps namespace clean - might aid the way you think,
improve code readability in particular applications
 *)

oIntersection[ls_] := Function[lsT, Intersection[ls, lsT]];

(* This particular usage involves an operator form of a variadic function which can give rise
 to well-definedness issues but my experience has been that prescribed usage is OK*)

oMemberQ[test_List] := Function[x, MemberQ[x]@test];

(* connects with a system operator form - awkward o usage *)

ifMemberQ[test_List] := Function[x, MemberQ[x]@test];

(* adds to linguistic feel *)

in[test_List] := Function[x, MemberQ[x]@test];

(* brief - very general *)

inQ[test_List] := Function[x, MemberQ[x]@test];

(* keeps with convention that Boolean functions ends in Q *)

ifInQ[test_List] := Function[x, MemberQ[x]@test];

(* adds to linguistic usage *)

Now road test them to see how they feel:

Intersection[test, find]
Select[test, oMemberQ@find]
Select[test, ifMemberQ@find]
Select[test, in@find]
Select[test, inQ@find]
Select[test, ifInQ@find]
test // Select@oMemberQ@find
test // Select@ifMemberQ@find
test // Select@in@find
test // Select@inQ@find
test // Select@ifInQ@find
test // oIntersection@find

(* {3,4} *)

Wait a few days; apply in different contexts; see which one emerges; place in init.m package.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.