[r7909]: / sandbox / jlf / samples / extension / array.cls  Maximize  Restore  History

Download this file

245 lines (213 with data), 10.5 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
/*
This script needs a modified ooRexx interpreter which support extension of predefined ooRexx classes.
*/
--::requires "extension/extensions.cls"
--::options trace i
/******************************************************************************/
::class "ArrayInitializer" mixinclass Object public
/*
Initializer (instance method) which takes into account the dimensions of the array.
If there is only one argument, and this argument is a string, then each word of the string is an item (APL-like).
.array~new(2,3)~of(1 2 3 4 5 6)
1 2 3
4 5 6
If there is only one argument, and this argument has the method ~supplier then each item returned by the argument's supplier is an item.
.array~new(2,3)~of(1~upto(6))
1 2 3
4 5 6
If there is only one argument, and this argument is a doer, then the doer is called for each cell to initialize.
Implicit arguments :
arg(1) : integerIndex : position of the current cell, from 1 to size.
arg(2) : arrayIndex : position of the current cell, in each dimension. Always an array, even if the number of dimensions = 1
The value returned by the doer is the item for the current cell.
.array~new(2,3)~of{10*integerIndex}
10 20 30
40 50 60
Otherwise, when more than one argument, each argument is an item as-is.
.array~new(2,3)~of(1,2,3,4,5,6)
1 2 3
4 5 6
If some arguments are omitted, then the corresponding item in the initialized arrat remains non-assigned.
.array~new(2,3)~of(1,,3,,5,6)
1 . 3
. 5 6
For me, there is a problem (bug ?) when the last arguments are explicitely omitted : they are not counted by the interpreter !
.array~new(2,3)~of(1,,3,,5,)
1 . 3
. 5 1
I was expecting this result, because I passed explicitely 6 arguments, 3 of them being omitted :
1 . 3
. 5 .
The items are a list of values that must be assigned to the cells of the array.
Rules inspired by APL :
If there are too many items, the extra items are ignored.
If there are fewer items than implied by the dimensions, the list of items is reused as
many times as necessary to fill the array.
.array~new(2,3)~of(1,2)
1 2 1
2 1 2
*/
::method of
use strict arg arg1, ... -- at least one argument
if self~dimension == 0 then raise syntax 93.900 array("Unknown number of dimensions")
-- Only one of the following variable will be used
arrayIn = .nil
doer = .nil
supplier = .nil
if arg() == 1 then do
if arg(1)~isA(.String) then arrayIn = arg(1)~subwords -- APL-like
else if arg(1)~hasMethod("functionDoer") then doer = arg(1)~functionDoer("use arg integerIndex, arrayIndex")~arity(2)
else if arg(1)~hasMethod("doer") then doer = arg(1)~doer
else if arg(1)~isA(.array), arg(1)~dimension == 1 then arrayIn = arg(1) -- omitted item in the array passed as argument ==> no item in the array under initialization
else if arg(1)~hasMethod("supplier") then supplier = arg(1)~supplier -- omitted items are ignored, because not available from the supplier
else arrayIn = arg(1, "a")
end
else arrayIn = arg(1, "a")
if arrayIn <> .nil then do
arrayInIndex = 1
arrayInSize = arrayIn~size
end
selfIntegerIndex = 1
selfArrayIndex = self~dimension~times{1} -- Initializes all indexes to 1
sourceEnd = 0
selfReuseIntegerIndex = 1
selfReuseArrayIndex = self~dimension~times{1} -- Initializes all indexes to 1
do while selfIntegerIndex <= self~size
if sourceEnd == 0 then do
-- if initialization with an array (covers the case of several arguments passed to the method)
if arrayIn <> .nil then do
if arrayInIndex > arrayInSize then sourceEnd = selfIntegerIndex
else do
if arrayIn~hasIndex(arrayInIndex) then self~put(arrayIn[arrayInIndex], selfArrayIndex)
arrayInIndex += 1
end
end
-- if initialization with a doer
else if doer <> .nil then do
-- next line is not really needed because when a coactivity is ended, there is not returned result
-- and this case is managed by the second test doer~isEnded below.
if doer~hasMethod("isEnded"), doer~isEnded then sourceEnd = selfIntegerIndex -- detection of end of coactivity
else do
if doer~arity >= 2 then doer~do(selfIntegerIndex, selfArrayIndex~copy)
else if doer~arity == 1 then doer~do(selfIntegerIndex)
else doer~do
if var("result") then self~put(result, selfArrayIndex)
-- next line is needed to offer the same behaviour for
-- .array~new(2,3)~of(1~upto(5))
-- .array~new(2,3)~of(1~generate.upto(5))
else if doer~hasMethod("isEnded"), doer~isEnded then sourceEnd = selfIntegerIndex -- detection of end of coactivity
end
end
-- if initialization with a supplier
else if supplier <> .nil then do
if \supplier~available then sourceEnd = selfIntegerIndex
else do
self~put(supplier~item, selfArrayIndex)
supplier~next
end
end
end
if sourceEnd <> 0 then do -- APL-like : If there are fewer items than implied by the dimensions, the list of items is reused as many times as necessary to fill the array.
if sourceEnd == 1 then leave -- can stop now because nothing to reuse (happens when you pass an empty provider)
if selfReuseIntegerIndex == sourceEnd then do
selfReuseIntegerIndex = 1 -- rewind
selfReuseArrayIndex~mapR{1} -- rewind all indexes to 1
end
if self~hasIndex(selfReuseArrayIndex) then self~put(self[selfReuseArrayIndex], selfArrayIndex)
selfReuseIntegerIndex += 1
self~incrementIndex(selfReuseArrayIndex)
end
selfIntegerIndex += 1
self~incrementIndex(selfArrayIndex)
end
return self
-- The shape of an array is an array which gives the size of each dimension (APL).
::method shape
shape = .array~new(self~dimension)
do i=1 to self~dimension
shape[i] = self~dimension(i)
end
return shape
::method incrementIndex
use strict arg arrayIndex -- updated in place
do i=self~dimension to 1 by -1
arrayIndex[i] += 1
if arrayIndex[i] <= self~dimension(i) then leave
arrayIndex[i] = 1
end
return arrayIndex -- for convenience
/******************************************************************************/
::class "ArrayPrettyPrinter" mixinclass Object public
-- Ex :
-- .array~of(1,"two", .array~of(1, "two", 3), .array~of(1, "two"))~ppRepresentation -- [[1,'two',[1,'two',3],[1,'two']]]
::method ppRepresentation
/*
Return a condensed string representation of the array.
Recursive arrays are supported. A reference *N is inserted in the representation,
where N is the number of levels to follow from the current position.
Ex :
a = .array~of("string1", "string2")
b = .array~of("string2")
b~append(a)
a~append(b)
a~append(a)
+<------------------------------------+<--------+
| ^ ^
V | |
+---------+---------+-----+--+--+ | |
| string1 | string2 | . | . | | |
+---------+---------+--|--+--|--+ | |
| | | |
| +---------->+ | Reference current level : *0
V |
+---------+-----+ |
| string2 | . | |
+---------+--|--+ |
| |
+----------------->+ Reference one level above : *1
say a~ppRepresentation
['string1','string2',['string2',*1],*0]
a~pipe(.console dataflow)
source:1,'string1'
source:2,'string2'
source:3,[v1='string2',['string1',v1,*1,*0]]
source:4,['string1',v1='string2',[v1,*1],*0]
a~pipe(.inject iterateBefore {item} recursive.0.memorize | .console dataflow)
source:1,v1='string1' | inject:1,v1
source:2,v1='string2' | inject:1,v1
source:3,[v1='string2',['string1',v1,*1,*0]] | inject:1,v1
source:3,[v1='string2',[v2='string1',v1,*1,*0]] | inject:1,[v2,v1,[v1,*1],*0]
source:4,[v1='string1',v2='string2',[v2,*1],*0] | inject:1,v1
source:4,['string1',v1='string2',[v1,*1],*0] | inject:1,v1
source:4,[v1='string1',v2='string2',[v2,*1],*0] | inject:1,[v2,[v1,v2,*1,*0]]
source:4,[v1='string1',v2='string2',[v2,*1],*0] | inject:1,[v1,v2,[v2,*1],*0]
*/
use strict arg separator=",", val=(self), stack=(.queue~new)
if val~isA(.array) /*, val~dimension == 1*/ then do
-- Remember : this code has been duplicated in pipe.rex, routine dataflow_representation.
level = stack~index(val)
if level <> .nil then return "*"level-1
stack~push(val)
-- each item of the array is inserted.
valstr = "["
sep = ""
do v over val
valstr ||= sep || self~ppRepresentation(separator, v, stack)
sep = separator
end
valstr ||= "]"
stack~pull
return valstr
end
else do
valstr = val~string
if val~isA(.String) then do
isnum = valstr~dataType("N")
if \isnum then valstr = "'"valstr"'" -- strings are surrounded by quotes, except string numbers
end
else do
isnum = .false
valstr = "("valstr")" -- to make a distinction between a real string and other objects
end
return valstr
end