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 <[email protected]>
>>>> 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 <[email protected]> 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 <[email protected]> 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 [email protected].
>>>>>>> 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 [email protected].
>>>>> 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
>>>> [email protected].
>>>> 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 [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/elixir-lang-talk/f2954d8a-d458-4e7d-abe7-567ab87cd3dd%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.