On 2/14/07, Frédéric van der Plancke < fvdp%40decis.be">fvdp
decis.be> wrote:
> roparzhhemon wrote:
> > --- In ocaml_beginners%40yahoogroups.com">ocaml_beginners
yahoogroups.com, Jon Harrop <jon
...> wrote:
> >
> >> The only notable exception to this rule crops up when you're traversing trees.
> >> Consider a function to substitute a value for a variable name in a symbolic
> >> expression:
> >>
> >> # let rec subst var value = function
> >> | `Add(f, g) -> `Add(subst var value f, subst var value g)
> >> | `Mul(f, g) -> `Mul(subst var value f, subst var value g)
> >> | `Int n -> `Int n
> >> | `Var v when v=var -> value
> >> | `Var v -> `Var v;;
> >> val subst :
> >> 'a ->
> >> ([> `Add of 'b * 'b | `Int of 'c | `Mul of 'b * 'b | `Var of 'a ] as 'b) ->
> >> ([< `Add of 'd * 'd | `Int of 'c | `Mul of 'd * 'd | `Var of 'a ] as 'd) ->
> >> 'b = <fun>
> >>
> >> This function might look ok but it is actually hugely inefficient because it
> >> copies the entire tree, even if no substitution is made.
> >>
> >
> > But is there a more efficient implementation in this case ?
> >
> >
> Yes, for instance the line
>
> | `Var v -> `Var v;;
>
> reconstructs a value from the existing one (and thus makes a copy), a cost you can avoir by writing
>
> | `Var v as x -> x;;
>
> so that it should be possible to return the original value when no substitution is made. OTOH, if substitutions are made, the part of the tree above the substitutions has to be copied, if you are working with immutable fields.
>
To expand on this, you need to communicate whether a substitution was
made, for example with a variant:
let rec subst var value = function
| `Add(f, g) ->
(match subst var value f, subst var value g with
None, None -> None
| Some a1, None -> Some (`Add (a1, g))
| None, Some a2 -> Some (`Add (f, a2))
| Some a1, Some a2 -> Some (`Add (a1, a2))
)
| `Mult(f, g) ->
(match subst var value f, subst var value g with
None, None -> None
| Some a1, None -> Some (`Mult (a1, g))
| None, Some a2 -> Some (`Mult (f, a2))
| Some a1, Some a2 -> Some (`Mult (a1, a2))
)
| `Int n -> None
| `Var v when v=var -> Some value
| `Var v -> None;;
This way only substitution paths are copied.
Best wishes,
Lukasz
.