Entirely different concepts. :-) 'zipping' a list means combining multiple lists into a single list of tuple/lists, which is what Enum/Stream does.
A Zipper data structure is essentially a tuple of two lists, for example: {[], [1, 2, 3, 4]} -> Move right -> {[1], [2, 3, 4]} -> Move right -> {[2, 1], [3, 4]} -> Move right -> {[3, 2, 1], [4]} -> Move left -> {[2, 1], [3, 4]} It is basically a way of iterating backwards and forward through a list (which is what a BF interpreter does to access its memory) in a very efficient, functional, immutable method. The left side holds the reversed 'data' to the left of your 'cursor', and the right holds the 'data' to the right of your 'cursor', where the cursor is just the current position along the entire structure. Zippers can also be more complicated as well, allowing you to efficiently and immutable walk along an entire tree structure back and forth efficiently. On Monday, August 29, 2016 at 4:28:46 PM UTC-6, Ben Wilson wrote: > > Can you elaborate on what you'd like out of a zipper library not found in > Stream/Enum? > > On Monday, August 29, 2016 at 10:45:55 AM UTC-4, OvermindDL1 wrote: >> >> Awesome! Been wanting a good Elixir Zipper library, the others I'd found >> were for Erlang and had different usages. :-) >> >> Easy to do it yourself though of course, but libraries also help document >> usage instead of just using a {[],[]} as a zipper. :-) >> >> On Monday, August 29, 2016 at 5:49:10 AM UTC-6, Brian Bugh wrote: >>> >>> Thanks for the great idea! I forgot about zippers. This was exactly what >>> I needed. There was a zipper tree library on hex but it was overkill for >>> what I needed, so I made my own: https://hex.pm/packages/zipper_list >>> >>> On Wednesday, August 24, 2016 at 10:00:29 AM UTC-5, OvermindDL1 wrote: >>>> >>>> Instead of holding 'data' and a 'dataptr' you could make the `data` >>>> data structure a zipper where the location of the zipper is the ptr. >>>> Would >>>> be very efficient. >>>> >>>> https://en.wikipedia.org/wiki/Zipper_(data_structure) >>>> >>>> There might even be a library or two on it on hex.pm. :-) >>>> >>>> >>>> On Tuesday, August 23, 2016 at 5:31:11 PM UTC-6, Brian Bugh wrote: >>>>> >>>>> Ah you're right! Very clever. That's awesome, thanks for explaining. >>>>> >>>>> The implementation I posted was doing the < and > commands backwards, >>>>> because I was thinking about it being cheaper to prepend to lists instead >>>>> of appending, but there's no way around it. >>>>> >>>>> The Wikipedia article about BF isn't very helpful. I've written an >>>>> interpreter successfully in Ruby, but I'm doing a clean room >>>>> implementation >>>>> of it in Elixir so I can't "cheat". >>>>> >>>>> The < and > commands assume you have a data pointer list of infinite >>>>> size, 0 based. Something like Array.new(n, 0) in Ruby. So if you run a BF >>>>> program like *<<<<<<+*, you'd end up with a data structure of [1, 0, >>>>> 0, 0, 0, 0], hence the required list manipulation. >>>>> >>>>> - Brian >>>>> >>>>> On Aug 23, 2016, at 4:24 PM, Torben Hoffmann <torben...@gmail.com> >>>>> wrote: >>>>> >>>>> Hi Brian, >>>>> >>>>> Looking at what your code does and what I suggested I don't see a >>>>> difference. >>>>> >>>>> Your command/2 will subtract 1 from dataprt and if dataptr < 0 then >>>>> you will reset dataptr to 0 and pop data. >>>>> >>>>> My solution took the liberty of saying "if dataptr=0 (before you >>>>> subtract) we have to pop data otherwise we subtract one from dataprt and >>>>> that is it". >>>>> That should do the same as your code, because if dataptr=0 when you >>>>> run your command/2 function you will end up with dataptr=-1 before >>>>> calling >>>>> do_trim, which will reset it to 0 and pop data. >>>>> >>>>> Reading the Brianfuck page on Wikipedia seems to suggest that the < >>>>> command should only decrement the dataptr, so I am not sure what the pop >>>>> you are doing with tl is good for. But then again, Brainfuck is not >>>>> supposed to be easy to understand... >>>>> >>>>> I actually can't believe that I am going to write the following... ;-) >>>>> >>>>> I would consider using two lists to hold the array of cells. Say, left >>>>> and right fields in your state. >>>>> Initially we would have left=[] and right= "a bunch of zeros". >>>>> Then we get something like this >>>>> >>>>> def command("<", state = %{left: []}) do >>>>> state # we silently do nothing when moving too far >>>>> end >>>>> >>>>> def command("<", state = %{left: [h|t]} do >>>>> %{state | left: t, right: [h | state.right]} >>>>> end >>>>> >>>>> # we always leave one element in right as that is the end of our >>>>> world >>>>> def command(">", state = %{right: [_]} do >>>>> state >>>>> end >>>>> >>>>> def command(">", state = %{right: [h|t]}) do >>>>> %{state | left: [h | state.left], right: t} >>>>> end >>>>> >>>>> def command("+", state = %{right: [h|t]}) do >>>>> %{state | right: [ h+1 | t ]} # should probably do some overflow >>>>> handling if we want to keep having a cell as a byte >>>>> end >>>>> ... >>>>> def command("[", state = %{right: [0|t]}) do >>>>> state >>>>> end >>>>> >>>>> def command("[", state) do >>>>> # this is where the fun starts, now we need to enter a mode where >>>>> we loop the code >>>>> # until we have balanced out [ and ]. >>>>> ... >>>>> end >>>>> >>>>> And already here I can see that using command with just two arguments >>>>> will probably be hurtful in the long run. >>>>> Perhaps the function should be called interpret and take a list of >>>>> chars as the first argument and simply peel off the first element. >>>>> When we reach a loop we have to start accumulating the commands in >>>>> case the loop has to run more than once. >>>>> But if we call a loop function that executes commands and return the >>>>> collected loop body as well as an updated state, then we simply have to >>>>> check the value at the data pointer when the loop has been collected. If >>>>> it >>>>> is zero we throw away the collected program, if not we re-insert the >>>>> collected program in front of the rest of the program and call interpret >>>>> again. >>>>> >>>>> Now I am almost tempted to go write the full interpreter, but luckily >>>>> for me it is late at my end right now, so I'll have to see if I am up for >>>>> it tomorrow!! >>>>> >>>>> Cheers, >>>>> Torben >>>>> >>>>> >>>>> On 23 August 2016 at 15:55, Brian Bugh <brai...@gmail.com> wrote: >>>>> >>>>>> It turns out that I think my assumption about how the BF interpreter >>>>>> works is wrong, but I want to continue this discussion because while my >>>>>> interpreter may be bad, I still don't know the answer to my Elixir >>>>>> question. :) >>>>>> >>>>>> Torben - thanks for the idea! I did try it with function pattern >>>>>> matching. However, the actual command is dataptr - 1 , so that needs >>>>>> to run first. *Then* if the dataptr is less than 0, the other check >>>>>> runs. The way you've suggested it in the first command assumes that >>>>>> the < command has already been run. :) >>>>>> >>>>>> Here's what I tried with pattern matching, which is even less >>>>>> comprehensible than that simple little if statement at the top. >>>>>> >>>>>> def command("<", state = %{data: data, dataptr: dataptr}) when >>>>>> is_list(data) and is_integer(dataptr) and dataptr >= 0 do >>>>>> do_trim(%{state | dataptr: dataptr - 1}) >>>>>> end >>>>>> >>>>>> def do_trim(state = %{data: data, dataptr: dataptr}) when dataptr < >>>>>> 0 do >>>>>> %{state | dataptr: 0, data: tl(data) } >>>>>> end >>>>>> >>>>>> def do_trim(state = %{data: data, dataptr: dataptr}), do: state >>>>>> >>>>>> Having been a programmer for 23 years, I'm a huge fan of writing code >>>>>> that my tired, burned out, >>>>>> working-on-the-weekend-because-release-date-is-Monday future self won't >>>>>> have to think about too hard to understand. >>>>>> >>>>>> Nothing I've come up with so far is as clear as that simple little if >>>>>> statement. >>>>>> >>>>>> >>>>>> >>>>>> On Tuesday, August 23, 2016 at 8:24:51 AM UTC-5, Torben Hoffmann >>>>>> wrote: >>>>>>> >>>>>>> Hi Brian, >>>>>>> >>>>>>> How about this? >>>>>>> >>>>>>> def command("<", state= %{dataprt: 0}) do >>>>>>> {:ok, %{state | data: tl(data)}} >>>>>>> end >>>>>>> >>>>>>> def command("<", state) do >>>>>>> {:ok, %{state | dataprt: state.dataptr - 1}} >>>>>>> end >>>>>>> >>>>>>> By using the pattern matching in the function clauses you get code >>>>>>> that reads like your description of the problem. >>>>>>> I.e., if the dataptr is zero pop the data stack otherwise decrement >>>>>>> the dataptr. >>>>>>> >>>>>>> Cheers, >>>>>>> Torben >>>>>>> >>>>>>> On 23 August 2016 at 15:08, Brian Bugh <brai...@gmail.com> wrote: >>>>>>> >>>>>>>> Here's another ugly one I came up with (with the complete function >>>>>>>> for reference) that is even worse. >>>>>>>> >>>>>>>> def command("<", state = %{data: data, dataptr: dataptr}) when >>>>>>>> is_list(data) and is_integer(dataptr) and dataptr >= 0 do >>>>>>>> dataptr = dataptr - 1 >>>>>>>> >>>>>>>> state = case state do >>>>>>>> %{dataptr: dataptr} when dataptr < 0 -> %{state | dataptr: 0, >>>>>>>> data: tl(data) } >>>>>>>> _ -> state >>>>>>>> end >>>>>>>> >>>>>>>> {:ok, %{state | dataptr: dataptr, data: data}} >>>>>>>> end >>>>>>>> >>>>>>>> Usually when I get stuck like this it means I'm overthinking >>>>>>>> something. >>>>>>>> >>>>>>>> Any suggestions? >>>>>>>> >>>>>>>> >>>>>>>> On Tuesday, August 23, 2016 at 7:51:22 AM UTC-5, Brian Bugh wrote: >>>>>>>>> >>>>>>>>> For fun and profit, I am writing a Brainf**k interpreter in Elixir. >>>>>>>>> >>>>>>>>> In one particular case, a data pointer should be decremented, and >>>>>>>>> if it's less than 0, it should be set to 0 and the top of the data >>>>>>>>> stack >>>>>>>>> should be popped off. >>>>>>>>> >>>>>>>>> I assumed that I should write something like this (which passes my >>>>>>>>> : >>>>>>>>> >>>>>>>>> dataptr = dataptr - 1 >>>>>>>>> >>>>>>>>> if dataptr < 0 do >>>>>>>>> dataptr = 0 >>>>>>>>> data = tl data >>>>>>>>> end >>>>>>>>> >>>>>>>>> but I get a compiler warning when I do this. It suggested that I >>>>>>>>> use the assignment form of *if*, which is fine, but I can't find >>>>>>>>> an elegant way to write the code now. This is what I came up with, >>>>>>>>> which >>>>>>>>> seems uglier and unnecessarily verbose for future readers. The first >>>>>>>>> form >>>>>>>>> above is much more readable. >>>>>>>>> >>>>>>>>> [dataptr | data] = if dataptr < 0 do >>>>>>>>> [0 | tl data] >>>>>>>>> else >>>>>>>>> [dataptr | data] >>>>>>>>> end >>>>>>>>> >>>>>>>>> Is there a better Elixir-way to write this? >>>>>>>>> >>>>>>>> -- >>>>>>>> You received this message because you are subscribed to the Google >>>>>>>> Groups "elixir-lang-talk" group. >>>>>>>> To unsubscribe from this group and stop receiving emails from it, >>>>>>>> send an email to elixir-lang-ta...@googlegroups.com. >>>>>>>> To view this discussion on the web visit >>>>>>>> https://groups.google.com/d/msgid/elixir-lang-talk/bfcb8bdf-aede-4955-ac50-a6ac6dde3e5a%40googlegroups.com >>>>>>>> >>>>>>>> <https://groups.google.com/d/msgid/elixir-lang-talk/bfcb8bdf-aede-4955-ac50-a6ac6dde3e5a%40googlegroups.com?utm_medium=email&utm_source=footer> >>>>>>>> . >>>>>>>> >>>>>>>> For more options, visit https://groups.google.com/d/optout. >>>>>>>> >>>>>>> >>>>>>> >>>>>>> >>>>>>> -- >>>>>>> http://www.linkedin.com/in/torbenhoffmann >>>>>>> @LeHoff >>>>>>> >>>>>> -- >>>>>> You received this message because you are subscribed to the Google >>>>>> Groups "elixir-lang-talk" group. >>>>>> To unsubscribe from this group and stop receiving emails from it, >>>>>> send an email to elixir-lang-ta...@googlegroups.com. >>>>>> To view this discussion on the web visit >>>>>> https://groups.google.com/d/msgid/elixir-lang-talk/743c6c3e-0def-4cf6-8dee-2338a19c0ef6%40googlegroups.com >>>>>> >>>>>> <https://groups.google.com/d/msgid/elixir-lang-talk/743c6c3e-0def-4cf6-8dee-2338a19c0ef6%40googlegroups.com?utm_medium=email&utm_source=footer> >>>>>> . >>>>>> >>>>>> For more options, visit https://groups.google.com/d/optout. >>>>>> >>>>> >>>>> >>>>> >>>>> -- >>>>> http://www.linkedin.com/in/torbenhoffmann >>>>> @LeHoff >>>>> >>>>> -- >>>>> You received this message because you are subscribed to a topic in the >>>>> Google Groups "elixir-lang-talk" group. >>>>> To unsubscribe from this topic, visit >>>>> https://groups.google.com/d/topic/elixir-lang-talk/_BPxgTc4VGM/unsubscribe >>>>> . >>>>> To unsubscribe from this group and all its topics, send an email to >>>>> elixir-lang-ta...@googlegroups.com. >>>>> To view this discussion on the web visit >>>>> https://groups.google.com/d/msgid/elixir-lang-talk/CABf3pCmnvMWt3xqvOC3Yim06XpLoSRkqzYqjPUoSp1HurGQbuw%40mail.gmail.com >>>>> >>>>> <https://groups.google.com/d/msgid/elixir-lang-talk/CABf3pCmnvMWt3xqvOC3Yim06XpLoSRkqzYqjPUoSp1HurGQbuw%40mail.gmail.com?utm_medium=email&utm_source=footer> >>>>> . >>>>> For more options, visit https://groups.google.com/d/optout. >>>>> >>>>> -- You received this message because you are subscribed to the Google Groups "elixir-lang-talk" group. To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-talk+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-talk/7a178281-5102-47ca-b42e-518969c27267%40googlegroups.com. For more options, visit https://groups.google.com/d/optout.