Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>I can't guarantee that this is idiomatic Clojure, but this is a functional version of your Python code, which should at least get you pretty close.</p> <pre><code>(def prog [ [:label :entry] [:load 0] [:load 1] [:add] [:store 0]]) (defn parse [stats] (let [ f (fn [[out-stats labels pc] stat] (if (= :label (first stat)) [out-stats (conj labels [(second stat) pc]) pc] [(conj out-stats stat) labels (+ 1 pc)])) init [[] {} 0] ] (reduce f init stats))) (println (parse prog)) </code></pre> <p>So I think you're correct that a fold is what you want. All functional folds walk a collection and "reduce" that collection into a single value. However, nothing says that the resulting single value can't also be a collection or, as in this case, a collection of collections.</p> <p>In our case, we are going to use the three-parameter version of reduce - this lets us provide an initial accumulator value. We need to do this because we are going to track a lot of state as we iterate across the collection of bytecodes, and the two-parameter version pretty much requires that your accumulator be similar to the items in the list. (c.f. <code>(reduce + [1 2 3 4])</code> )</p> <p>When working with a functional fold, you need to think in terms of what you are accumulating, and how each element in the input collection contributes to that accumulation. If you look at your Python code, there are three values that can be updated on each turn of the loop:</p> <ul> <li>The output statements (<code>self.code</code>)</li> <li>The label mapping (<code>self.labels</code>)</li> <li>The program counter (<code>pc</code>)</li> </ul> <p>Nothing else is written during the loop. So, our accumulator value will need to store those three values.</p> <p>That previous bit is the most important part.</p> <p>Once you have that, the rest should be pretty easy. We need an initial accumulator value, which has no code, no label mappings, and a PC that starts at 0. On each iteration, we will update the accumulator in one of two ways:</p> <ul> <li>Add a new label mapping</li> <li>Add a new output program statement, and increment the program counter</li> </ul> <p>And now, the output:</p> <pre><code>[[[:load 0] [:load 1] [:add] [:store 0]] {:entry 0} 4] </code></pre> <p>That's a 3-element vector. The first element is the program. The second element is the label mappings. The third element is the next PC value. Now, you might modify parse to only produce two values; that's not an unreasonable thing to do. There are reasons you might not want to do it, but that's more an issue of API design than anything. I'll leave it as an exercise to the reader.</p> <p>I should also mention that, initially, I had omitted the let block and had simply inlined the named values. I decided to pull them out to hopefully increase readability. Again, I don't know which is more idiomatic. That might be more of a per-project convention.</p> <p>Finally, I don't know if monads have really taken off in the Clojure community, but you could also create a monad for bytecode parsing, and define the operations "add-statement" and "add-label" to be values in that monad. This would greatly increase the set-up complexity, but would simplify the actual parsing code. In fact, it would allow your parsing code to look fairly procedural, which may or may not be a good thing. (don't worry, it's still functional and side-effect free; monads just let you hide plumbing.) If your Python sample is pretty representative of the kind of data you need to process, then monads are almost certainly unnecessary overhead. On the other hand, if you actually have to manage much more state than indicated by your sample, then monads might help to keep you sane. </p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload