8

In many programming languages there are functions that remove an element from a set while giving back that element. E.g. I'd like to have a function FetchFromStack that does the following:

stack = {one, two, three};
element = FetchFromStack[stack]
stack

one

{two, three}

Of course I can implement it like:

If[stack==={}, element=False, element = stack[[1]]]
If[Length[stack]>1, stack = stack[[2;;]], stack={} ]

but this seems much too hacky of a solution to me. Is there a proper routine in Mathematica that does this? If not, what would be the best way to implement it?

J. M.'s missing motivation
  • 124,525
  • 11
  • 401
  • 574
Kagaratsch
  • 11,955
  • 4
  • 25
  • 72

7 Answers7

9

Maybe something like:

push[stackID_][e_] := Last[$Stack[stackID] = {$Stack[stackID], e}]

pop[stackID_] := Replace[$Stack[stackID],
    {
    {s_, e_} :> ($Stack[stackID] = s; e),
    _ -> Missing["Empty"]
    }
]

stack[stackID_] := $Stack[stackID]

empty[stackID_] := Quiet[Unset@$Stack[stackID];, Unset::norep]

$Stack[_]={};

For example, push 1 through 5 to stack id 1:

push[1] /@ Range[5]

{1, 2, 3, 4, 5}

Then, pop 6 elements from stack id 1:

Table[pop[1], {6}]

{5, 4, 3, 2, 1, Missing["Empty"]}

Note that the stack is implemented as a nested list instead of a flat list for efficiency reasons. So:

empty[1]
push[1] /@ Range[3];
stack[1]

{{{{}, 1}, 2}, 3}

Changing the implementation to a flat list is simple, although you will take a performance hit for long stacks ($O(n)$ vs $O(1)$).

Carl Woll
  • 130,679
  • 6
  • 243
  • 355
  • Is there a way to read out each stack without adding or removing anything? – Kagaratsch Jul 21 '17 at 15:02
  • I see, so the stack is nested. But how exactly does that contribute to efficiency? – Kagaratsch Jul 21 '17 at 15:31
  • What would make an unnested stack slower compared to this? Doesn't adding or removing an element require the whole list to be moved in memory and be rewritten either way? – Kagaratsch Jul 21 '17 at 15:36
  • 1
    I couldn't find a good question/answer discussing why Append/AppendTo is bad, but the short answer is that AppendTo[list, elem] makes a copy of list (hence $O(n)$) while list = {list, elem} does not (hence $O(1)$). – Carl Woll Jul 21 '17 at 15:39
  • This is huge! I did not know that. Is it true that list=list[[1]] does not make a copy of anything either? Then the stack should be unloaded in reverse, to improve performance. And I think that is exactly what your code does. – Kagaratsch Jul 21 '17 at 15:44
  • 1
    @Kagaratsch. For more on this "linked-list" structure, see the answer here. – march Jul 21 '17 at 16:02
  • Is there a quick way to get the number of elements on the stack? I figure stack[1]//Flatten//Length won't do in terms of performance, and Depth[stack[1]] goes after the depth in the elements as well as the stack. – Kagaratsch Nov 18 '17 at 03:19
  • 1
    @Kagaratsch If you need this I think you could add a counter, and increment/decrement it in the definition of push/pop. – Carl Woll Nov 18 '17 at 21:13
  • I've taken this idea, combined with a few others, fleshed it out with some more functions, and created a package. The StackLength function is unfortunately crude, so don't use it too frequently. Package is at https://github.com/truculentmath/QueueStack – Kevin O'Bryant Jan 16 '20 at 18:38
5

Here is one possible implementation that is somewhat Mathematica-idiomatic. Using your list:

stack = {one, two, three};

ClearAll@fetchFromStack
Attributes[fetchFromStack] = HoldAll
fetchFromStack[stack_Symbol /; Evaluate[stack] === {}] := (stack = {};False)
fetchFromStack[stack_Symbol : {elem_}] := (stack = {}; elem)
fetchFromStack[stack_Symbol] := Module[{x = stack[[1]]},
  stack = stack[[2 ;;]];
  x
 ]

Then:

stack = {one, two, three};
Table[{fetchFromStack[stack], stack}, {5}]
(* {{one, {two, three}}, {two, {three}}, {three, {}}, {False, {}}, {False, {}}} *)
march
  • 23,399
  • 2
  • 44
  • 100
  • @Kagaratsch. Thanks, but that accept was too fast! I feel like this question could get a lot of attention and have some really good answers, and it won't get as much attention if there's an accept on it! – march Jul 21 '17 at 02:52
  • 1
    OK, removed it for now. :) Will put it back tomorrow, if no one shows up with a different implementation that does this orders of magnitude faster. – Kagaratsch Jul 21 '17 at 02:53
  • The problem with this approach is if you have two stacks, stack1 and stack2, and you try something like: curStack = stack1; fetchFromStack[curStack] it won't work. – Carl Woll Jul 21 '17 at 03:34
  • @CarlWoll. I'm not sure I follow, so I might not understand something about how "stack"s are supposed to be interact. If stack1 = {one, two, three}, and we run curStack = stack1;, then the definition curStack = {one, two, three} is associated with curStack, and it gets updated using fetchFromStack as normal. Is the problem that fetchFromStack[currStack] won't update stack1? Is that the desired functionality? – march Jul 21 '17 at 03:58
  • Right, I think stack1 should get updated. – Carl Woll Jul 21 '17 at 04:03
  • @CarlWoll. I don't think there's an obvious way to fix my answer to have that functionality. Your answer is nice. – march Jul 21 '17 at 16:10
5

One more:

ClearAll[fetch];
SetAttributes[fetch, HoldFirst];
fetch[stack_Symbol] := If[Length[stack] > 0,
  Module[{h}, {{h}, stack} = TakeDrop[stack, 1]; h],
  False
]

Now

stack = {one, two, three};
fetch[stack]
fetch[stack]
fetch[stack]
fetch[stack]
(* one *)
(* two *)
(* three *)
(* False *)
JEM_Mosig
  • 3,003
  • 15
  • 28
4
ClearAll[fetch]
stack={one,two,three};
fetch[stack_List/;stack =!= {}]:={First@stack,Rest@stack}
fetch[stack_List]:={{},{}};

And now

Mathematica graphics

The first definition could also be written as

 fetch[stack_List /; Length@stack > 0] := {First@stack, Rest@stack}
Nasser
  • 143,286
  • 11
  • 154
  • 359
3

I was thinking of how to not explicitly check for empty set case, that's what I came up with:

SetAttributes[fetchFromStack, HoldFirst];
fetchFromStack[stack_] := Module[{pop},
  {stack, pop} = Reap@ReplacePart[stack, 1 :> (Sow[First@stack]; Nothing)];
  FirstCase[pop, {x_} :> x, False]
  ]

stack = {one, two, three}
fetchFromStack[stack]
fetchFromStack[stack]
fetchFromStack[stack]
fetchFromStack[stack]
(*one, two, three, False*)
swish
  • 7,881
  • 26
  • 48
3
SetAttributes[fetch, HoldFirst];
fetch[s_ /; Length@s > 0] := With[{o = First@s}, s = Rest@s; o]
fetch[_] := False

stack = {1, 2, 3, 4};

Table[{fetch@stack, stack}, {Length@stack + 1}] // MatrixForm

enter image description here

eldo
  • 67,911
  • 5
  • 60
  • 168
3

New with Mathematica 12.1, you can use the "Queue" DataStructure. For your example:

ds = CreateDataStructure["Queue"];
ds["Push", #]& /@ {one, two, three};

Then, the elements are:

ds["Elements"]

{one, two, three}

Pop the first element:

ds["Pop"]

one

The queue now only has two elements:

ds["Elements"]

{two, three}

You can also query the length of the queue with:

ds["Length"]

2

or only peek at the first element with:

ds["Peek"]
ds["Elements"]

two

{two, three}

Carl Woll
  • 130,679
  • 6
  • 243
  • 355