Say I have a the typical Mathematica linked list structure defined like so:
Attributes[Pair] = {HoldAllComplete};
Nil = Pair[];
Cons = Pair[#1, #2]&;
It's well-known that turning a regular List
into a linked list is easy:
ToLinkedList[list_List] := Fold[Cons[#2, #1]&, Nil, Reverse@list];
With some caveats that aren't relevant to this problem, converting back to a regular list is even easier:
Pair /: Normal[p_Pair] := List @@ Flatten[p];
We can use this to make a "constructor":
LinkedList[args___] := ToLinkedList@{args};
Right now, the output looks pretty terrible
In[43]:= ToLinkedList@{1, 2, 3}
Out[43]= Pair[1, Pair[2, Pair[3, Pair[]]]]
It would be really nice if we could have output that looks like LinkedList[1, 2, 3]
instead. Attempts to do simple things with Format
have provided me with no joy. For instance this looks great:
Format[p : Pair[_, _]] :=
Row[{"LinkedList", "[", Row[p // Normal, ","], "]"}, BaseStyle -> "Output"];
Format[Nil] := Row[{"Nil"}, BaseStyle -> "Output"]
In[45]:= LinkedList[1, 2, 3]
Out[45]= LinkedLits[1, 2, 3]
But it completely falls apart when you try to copy and paste. Indeed, I had to type the stuff after Out
by hand, because when I tried to paste into the text area, what actually showed up was:
Row[{"LinkedList", "[", Row[{1, 2, 3}, ","], "]"},
BaseStyle -> "Output"]
This was not really what I wanted. Cutting and pasting inside a notebook appears to work OK, but when I try to evaluate a pasted expression as input, I get awful stuff like this:
MakeExpression::boxfmt: InputForm in MakeExpression[FormBox[RowBox[{Row,[,RowBox[{RowBox[{<<3>>}],,,RowBox[{<<3>>}]}],]}],InputForm],InputForm] is not a box formatting type. A box formatting type is any member of $BoxForms. >>
I know I could use MakeBoxes
to do what I want, but that only works for StandardForm
or TraditionalForm
, and this seems like a perfectly sensible textual representation. I have a feeling I'm using Format
completely wrong, but I'm not sure what using it right looks like.
EDIT in response to rcollyer: I have tried the Notation`
package, but it's box-oriented, and I can't figure out how to flatten out the Pair
structures for outputting inside the LinkedList
.
EDIT again in response to ruebenko: you're right; it's been fixed.
EDIT to fix an arguable bug in the implementation of Normal
for Pair
s, discovered while playing around trying to create a custom format. As it stands right now:
In[49]:= LinkedList[LinkedList[1, 2], LinkedList[3, 4], LinkedList[5, 6]]
Out[49]= LinkedList[1, 2, 3, 4, 5, 6]
I don't want this auto-flattening behavior for Normal
in any event, but it's particularly terrible in the case of formatting output because the output is totally wrong. Here's a fix:
FromLinkedList[list_Pair] :=
NestWhileList[List @@ #[[2]] &, {Null, list}, #[[2]] =!= Nil &][[2 ;;, 1]];
Pair /: Normal[p_Pair] := List @@ FromLinkedList[p]
This is a bit uglier, especially that Null
placeholder at the beginning, but it does the right thing:
In[63]:= LinkedList[LinkedList[1, 2], LinkedList[3, 4], LinkedList[5, 6]]
Out[63]= LinkedList[LinkedList[1, 2], LinkedList[3, 4], LinkedList[5, 6]]
It's the best I could come up with; other solutions involved Reap
and Sow
, which seemed like a real shame when working with a nice functional data structure like this one.
Notations`
package for this? $\endgroup$ToLinkedList[list_List] := Fold[Cons[#2, #1] &, Nil, Reverse@list];
otherwise I am not sure what you would like to do? $\endgroup$