6

I'm unable to understand this example given in manpage of dc:

$ dc  
 1 0:a 0Sa 2 0:a La 0;ap  
 1  

To me answer should be 2 because:

  1. 1 0:a
    Here we store 1 at 0th position of array a.

  2. 0Sa
    Now we push 0 to the stack of register a.

  3. 2 0:a Now here again we store 2 at 0th position of array a thereby overwriting the previous 1 stored at that location.

  4. La
    Now we pop the 0 stored on stack of register a and push it to main stack.

  5. 0;a
    Now we again push 0 to the main stack and then pop it to use as an array index and so we push the 2 stored at 0th location of array a to the main stack.

  6. p
    Now we print the top of main stack which is 2. So answer should be 2.

What am I missing?

PS- I wanted to use dc as a tag but looks like it doesn't exist and it's compulsory to use at least one tag so used debian (my workstation OS).

Runium
  • 28,133
  • 5
  • 50
  • 71
rootkea
  • 186
  • 11

3 Answers3

8

As in intermixing arrays and stacks. In the example register a is used both as an array and a as a stack.

1 0:a 
0 Sa 
2 0:a 
La 
0;a p
  1. First :a - register a is treated as an array.
  2. Then Sa - register a is treated as a stack. In effect pushing the array from from pt 1. down and creating a new array. As by man: Note that each stacked instance of a register has its own array associated with it.
  3. Then :a - register a is treated as an array. Pushing the prior value and the first array value down.
  4. Then La - register a is treated as a stack. To get to the fist stacked array throws away the a[0]=2 as it is an array.
  5. Then ;a - register a is treated as an array. Now there is only one value left, the first array value added to a which is 1.

Se bottom of answer for some more examples.


As per comment:

«How many arrays and stacks are there per register? I thought one register has one stack and one separate array.»

A Stack:

The stack in dc is a push-down-stack or LIFO (Last In First Out). Same as plates in a restaurant.

      ,------ pop / push - take / leave 
    /
   |
   v
[-----] top
[-----]           ...                 ...                .
[-----]          [-----]             [-----]            ..
 (main)        (register-a)        (register-b)        ...

We have a main stack or work stack which is the one used unless a operation demanding a register is specified. Each register has it's own stack.'

Basic register operations:

  • Sr: pop one value from main stack and push it onto stack specified by register r. Both stacks modified.
  • Lr: pop one value from register stack specified by r and push it onto main stack. Both stacks modified.
  • sr: pop one value from main stack and write it to register r. In effect change topmost value in stack specified by register r. If no value in that stack – add it. main stack modified. Register stack preserved beside changed value.
  • lr: read value from register r. In effect the topmost value if there are several. main changed. Register stack preserved.
  • :r: pop two values from main stack and use first, topmost, as index for where to store second value in array at register r. main changed. Register stack preserved.
  • ;r: pop one value from main stack and use it as index from where to read from current array in register specified by r. main changed. Register stack preserved.

Stack and arrays intermingled

One way to look at is in pairs. When you start out all the registers are empty. When you add an element to the stack by Sr, you hide any underlying elements in that stack. Say you do:

1 Sx
2 Sx
4 Sx

x = (S)  4     VISIBLE
    (S)  2     HIDDEN
    (S)  1     HIDDEN

Now you can change the value in register x, that is; change the topmost element, by sx, and you can read by lx – without altering the number of elements in the stack:

lx p  # Read value in register x - in effect read topmost element from stack.
4     # Printed value by p.
3 sx  # Change value in register x - in effect change topmost element in stack.

x = (S)  3     VISIBLE
    (S)  2     HIDDEN
    (S)  1     HIDDEN

If you decide to add array elements things start to go in a more complex direction.

4 1:x
5 2:x
    
x = [A] 
        [2]=5  VISIBLE
        [1]=4  VISIBLE
    (S)  3     VISIBLE
    (S)  2     HIDDEN
    (S)  1     HIDDEN

Now we have added values to the current array in the stack. We can read and modify any VISIBLE elements.

44 1:x
55 2:x
33 sx

1;x p # Read and print index 1
44

lx p  # Read and print stack top.
33

x = [A] 
        [2]=55  VISIBLE
        [1]=44  VISIBLE
    (S)  33     VISIBLE
    (S)  2      HIDDEN
    (S)  1      HIDDEN

If we then add a stack element one way can say that the stack frame is non-expandable as we have added values to the array above it. Thus a new stack frame is added.

6 Sx
7 Sx

x = (S)  7      VISIBLE
    (S)  6      HIDDEN
    [A] 
        [2]=55  HIDDEN
        [1]=44  HIDDEN
    (S)  33     HIDDEN
    (S)  2      HIDDEN
    (S)  1      HIDDEN

If we now try to acces the last array it is hidden. In effect we read from an empty array and the result is the default value 0. We can modify the register value 7 by sr, but not access the array two levels down unless we get rid of the two stack elements above.

If we now decide to add some array elements, they are added to a new array positioned (as a paired array) with the top stack element.

8 1:x
9 2:x

x = [A]
        [2]=9   VISIBLE
        [1]=8   VISIBLE
    (S)  7      VISIBLE
    (S)  6      HIDDEN
    [A] 
        [2]=55  HIDDEN
        [1]=44  HIDDEN
    (S)  33     HIDDEN
    (S)  2      HIDDEN
    (S)  1      HIDDEN

Now if we do a pop of the stack we pop of 7 but as there is an array in between (so to speak) it is also removed.

Lx p  # Pop + print top stack element.
7     # Value printed.

x = (S)  6      VISIBLE
    [A] 
        [2]=55  HIDDEN
        [1]=44  HIDDEN
    (S)  33     HIDDEN
    (S)  2      HIDDEN
    (S)  1      HIDDEN

The array with 8 and 9 is gone. The stack element with value 6 is visible. But the underlying array is blocked. A read by 1;x p yield 0.

In a way we can say the stack elements are blocking, whilst the arrays are opaque. The arrays are sort of hanging on to the stack elements.

We need to do yet another pop from the stack to reveal the underlying stack element + array.

Lx p  # Pop + print top stack element.
6     # Value printed.

x = [A] 
        [2]=55  VISIBLE
        [1]=44  VISIBLE
    (S)  33     VISIBLE
    (S)  2      HIDDEN
    (S)  1      HIDDEN

In conclusion one can say that the number of arrays and stack are not limited to one per register.

How many arrays and stacks are there per register?
– Depends on how many alternating Sr and :r operations you do on that register.

Another way of looking at it is that there is only one stack but multiple arrays iff we add stack elements between adding array elements ...

Yet another way is to say that at any given moment the current array is not

[register][array]

but

[register][stack-element][array]

which gives:

[register][stack-element][array][...]
[register][stack-element][array][1]
[register][stack-element][array][0]

and that the stack-element part is opaque, read only, etc. Though in that case we also have to remember that we do not need a value for stack element. It is OK to only add array values to a register.

Or each stack element has is paired with a zero filled array that we can modify:

1 Sx
2 Sx
3 Sx
4 1:x
5 2:x
6 Sx
7 Sx
8 1:x
9 2:x

x = (S)  7   +   A[0]=0   A[1]=8   A[2]=9   A[3]=0  ...  A[2048]=0
    (S)  6   +   A[0]=0             ...                  A[2048]=0
    (S) 33   +   A[0]=0   A[1]=4   A[2]=5   A[3]=0  ...  A[2048]=0
    (S)  2   +   A[0]=0             ...                  A[2048]=0
    (S)  1   +   A[0]=0             ...                  A[2048]=0

Hope it made it a bit more clear.


Some examples


$ dc
[ein]  0:a
[zwei] Sa
[drei] 0:a

0;ap   # Copy + print index 0 of topmost array in register a
drei

Lap    # Throws away [drei] and pops+prints first element in stack*
zwei

0;ap   # Copy + print index 0 of first array 
ein

$ dc
[uno]    0:a  # Array a(1)
[dos]    1:a  # Array a(1)
[tres]   2:a  # Array a(1)

[cuatro] Sa   # Array a(2)
[cinco]  Sa   # Array a(2)
[seis]   Sa   # Array a(2)

[siete]  0:a  # Array a(3)
[ocho]   1:a  # Array a(3)
[nueve]  2:a  # Array a(3)

Laf      # Throws away Array 3 to get to first stack array, Array 2.
seis

Laf
cinco
seis

Laf
cuatro
cinco
seis

2;af      # Now we're at the first array, Array 1.
tres
cuatro
cinco
seis

1;af
dos
tres
cuatro
cinco
seis

0;af
uno
dos
tres
cuatro
cinco
seis
Runium
  • 28,133
  • 5
  • 50
  • 71
  • This is a little bit confusing for me. How many arrays and stacks are there per register? I thought one register has one stack and one separate array. – rootkea Jan 06 '16 at 14:22
  • wow. way to go. you use `dc` a lot? you should get the BSD version. – mikeserv Jan 07 '16 at 07:40
  • @mikeserv: A little from time to time. I'd like to learn it better. On the ever so long to-do-list. – Runium Jan 07 '16 at 21:47
  • @Sukminder Thank you for that detailed illustration! For me personally *"Or each stack element has is paired with a zero filled array"* + the code snippet following that was the all dc-doubts clarifying instance. Wish I could upvote twice! – rootkea Jan 11 '16 at 20:51
5

You have to remember that dc is a compiler - and a crazy old one at that. It's a kind of machine language - a stack-oriented calculator. It's pretty powerful, but its interface is not designed for you - it's designed to efficiently process the instructions that you write in some other language after all of the user-friendliness has been machine processed out of it.

dc doesn't store data in an intuitive way. dc stuffs stuff. When it reads a new input if it does not immediately execute it the value is just tossed on a stack. Anything in the stack already is pushed down - the newest on top and the oldest at the bottom. You have to process the newest stack members to retrieve the oldest.

A lot of people can get that much. Stacks aren't so strange as all that, after all - it's kind of like a laundry basket. But that's pretty one-dimensional as well, and dc takes it a lot further than that.

So there is the input stack - the main stack. It's where all of dc's instructional input and output go by default. It's what you work when you give it commands. But there are also all of the other registers - at least 256 of them. Each of these also is a stack unto itself.

You typically work with the registers with the [Ss]ave and [Ll]oad commands. To save the top value on the main stack as a scalar value into register a you do sa. You can then load this scalar value back into the top of the main stack at any time with la. Well, you can as long as the current instance of the register a remains current, that is.

To create a new scalar instance of register a atop the old - where your scalar value remains - you can use Sa. That command pops the main stack and stacks the register a stack. Unlike la, the La command is a destructive Load of the register - when the scalar value is popped onto the main stack with that command anything in that instance of the register is destroyed and any previous instance of the register is once again accessible.

That much, too, is easy enough to get a handle on with a little practice - its just like the main stack but one for each register. But there is a further dimension to each register - their arrays.

Each instance of each register gets an array. I think the default size is 2048 indices per - though I have often wondered how deep the stacks go and can only say it's pretty deep. When you instantiate a new scalar value for a register you don't just push down its scalar value, but you push down its array as well. The new instance has a new array, and the old instance's array remains intact and will be as accessible to you as the scalar value is after you pop the new instance.

Array index access is a little tricky. In the first place, almost all main stack operations are destructive anyway. And the only way to access an array index is to pull its value from the main stack and then call it up. Because your index reference is destroyed at that point, it can be difficult to recall it. o and O can be useful for retaining an index counter - but don't try them with indices 1 or 0.

Anyway, the register's array and its stack are not independent, but are rather interdependent and multidimensional. With practice, and patience, and a little super-human determination weirdly cool things can be done with it. Good luck.

rootkea
  • 186
  • 11
mikeserv
  • 57,448
  • 9
  • 113
  • 229
  • *"In any case, when you instantiate a new scalar value for a register you don't just push down its scalar value, but you push down its array as well."* I think this is incorrect if we use `s` to instantiate a new scalar value for a register as it doesn't push down the older scalar value of register rather replaces it. `S` will, on the other hand push down the older scalar value as it pushes that scalar value onto the stack associated with the corresponding register. – rootkea Jan 11 '16 at 20:32
  • *"But index access is also destructive in that when you pull a value out of an index that index is afterward empty."* I think this is incorrect too as when we use `;` command to access the array element, the element gets *copied* to main stack meaning retaining the original copy in an array. – rootkea Jan 11 '16 at 20:37
  • @rootkea - that's what it says - `s` replaces and `S` pushes it down - while instantiating a new instance atop the stack. and I thought for sure puling an index emptied the index...? It's been a little while, though, admittedly. – mikeserv Jan 11 '16 at 21:17
  • I think `s` doesn't instantiate a new instance atop the stack. Is that correct? – rootkea Jan 11 '16 at 21:23
  • @rootkea - No, `s` replaces the current top-of stack's scalar. `S` pushes it down and instantiates a new instance of the register atop the old. In the both cases you pull from the main stack, in the first case you overwrite a value, but in the second you create a new value and stack it atop the old. – mikeserv Jan 11 '16 at 21:26
  • Thank you for that comment. So `s` does instantiates a new instance atop the stack but doesn't push down the scalar value of stack and it's associated array in doing so. If I understood this properly then I still find original comment somewhat contradicting. *""In any case, when you instantiate a new scalar value for a register you don't just push down its scalar value, but you push down its array as well.""* – rootkea Jan 11 '16 at 21:37
  • 2
    @rootkea - No: `s` does *not* do a new instance. It replaces the scalar value for the register. `S` gets a *new* instance and stacks it atop the old one. `L` destroys the new one and pops it off, bringing the old one to the top of the stack once more. – mikeserv Jan 11 '16 at 21:49
0

I ran into the same question, but carefully rereading the text surrounding the example in the man page itself was enough:

   :r     Will  pop  the top two values off of the stack.  The old sec-
          ond-to-top value will be stored in the array  r,  indexed  by
          the old top-of-stack value.

   ;r     Pops  the top-of-stack and uses it as an index into the array
          r.  The selected value is then pushed onto the stack.

   Note that each stacked instance of a  register  has  its  own  array
   associated  with  it.   Thus  1  0:a 0Sa 2 0:a La 0;ap will print 1,
   because the 2 was stored in  an  instance  of  0:a  that  was  later
   popped.

The final paragraph here fully answers your question. The 2 was stored in an instance of 0:a that was later popped (and silently discarded when La put the scalar value from register a onto the main stack).

Wildcard
  • 35,316
  • 26
  • 130
  • 258