[m-dev.] for review: add a new pred to array.m
Fergus Henderson
fjh at cs.mu.OZ.AU
Tue Mar 30 17:09:09 AEST 1999
On 30-Mar-1999, Thomas Charles Conway <conway at hydra.cs.mu.oz.au> wrote:
>
> For anyone to review.
> As suggested by the comments, there may be a (tricky) more efficient
> implementation (same O(), better constant factors/better memory usage).
> If anyone wants to improve the implementation, I'm quite open to
> suggestions.
> --- NEWS 1999/03/24 05:32:17 1.140
> +++ NEWS 1999/03/30 05:38:06
> @@ -39,6 +39,10 @@
> Changes to the Mercury standard library:
> ****************************************
>
> +* We've added a new higher order predicate the array module in the
> + standard library:
> + array__map/3.
> +
> * We've added a new predicate to the Mercury standard library:
> bag__count_value/3.
How about just
* We've added some new predicates to the Mercury standard library:
bag__count_value/3,
array__map/3.
?
> + % array__map(Closure, OldArray, NewArray) applys `Closure' to
> + % each of the elements of `OldArray' to create `NewArray'.
> +:- pred array__map(pred(T, U), array(T), array(U)).
> +:- mode array__map(pred(in, out) is det, array_di, array_uo) is det.
If you want short names for type variables, I much prefer T1, T2, ...
rather than T, U, ...
Any particular reason why this is a predicate, rather than a function?
The old rationale for using predicates was for debugging with Prolog,
but that doesn't apply now.
> + % The current implementation of array__map converts to a list,
> + % invokes list__map, then converts back to an array representation.
> + % It would be nicer to be able to do the map on each element
> + % maintaining the same array skeleton. Unfortunately this is
> + % tricky since the intermediate arrays have some elements of
> + % one type, and some of the other.
Yes, that would be tricky -- particularly for the accurate garbage collector
and the debugger. I don't think it would be worthwhile to try to make them
somehow cope with mixed arrays like this.
So a zero-copy implementation is tricky, in general.
However, it's certainly quite possible to do better than your two-copy
implementation. A one-copy implementation should be straight-forward, no?
array__map(Closure, OldArray, NewArray) :-
( array__semidet_lookup(OldArray, 0, Elem) ->
array__size(OldArray, Size),
array__init(Size, Elem, NewArray0),
array__map_2(0, Size, Closure, OldArray,
NewArray0, NewArray)
;
array__make_empty_array(NewArray)
).
array__map_2(N, Size, Closure, OldArray, NewArray0, NewArray) :-
( N >= Size ->
NewArray = NewArray0
;
array__lookup(OldArray, N, OldElem),
Closure(OldElem, NewElem),
array__set(NewArray0, N, NewElem, NewArray1),
array__map_2(N + 1, Size, Closure, OldArray,
NewArray1, NewArray)
).
If you want to get tricky, you could special-case this for the case
where the two types are the same, using a dynamic type test:
array__map(Closure, OldArray, NewArray) :-
( univ_to_type(univ(OldArray), NewArray0) ->
array_size(NewArray0, Size),
array__map_3(0, Size, Closure, NewArray0, NewArray)
; array__semidet_lookup(OldArray, 0, Elem) ->
array__size(OldArray, Size),
array__init(Size, Elem, NewArray0),
array__map_2(0, Size, Closure, OldArray,
NewArray0, NewArray)
;
array__make_empty_array(NewArray)
).
% this is the same as before
array__map_2(Closure, OldArray, N, Size, NewArray0, NewArray) :-
( N = Size ->
NewArray = NewArray0
;
array__lookup(OldArray, N, OldElem),
Closure(OldElem, NewElem),
array__set(NewArray0, N, NewElem, NewArray1),
array__map_2(N + 1, Size, Closure, OldArray,
NewArray1, NewArray)
).
% this is the same as array__map_2,
% except it uses NewArray0 instead of OldArray
array__map_3(Closure, N, Size, NewArray0, NewArray) :-
( N = Size ->
NewArray = NewArray0
;
array__lookup(NewArray0, N, OldElem),
Closure(OldElem, NewElem),
array__set(NewArray0, N, NewElem, NewArray1),
array__map_3(N + 1, Size, Closure, NewArray1, NewArray)
).
If you want to be *really* tricky, you could try using unsafe_promise_unique
to combine array__map_2 and array__map_3. That is, replace the call
to array__map_3 with
array__map_2(0, Size, Closure, NewArray0,
promise_unique(NewArray0), NewArray)
and then you can delete the code for array__map_3.
The call to promise_unique is a lie, but it should be safe, because
each element of the old array won't be reused until after its last use.
However, this is probably being *too* tricky ;-)
--
Fergus Henderson <fjh at cs.mu.oz.au> | "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh> | of excellence is a lethal habit"
PGP: finger fjh at 128.250.37.3 | -- the last words of T. S. Garp.
More information about the developers
mailing list