I'm sorry, it appears that I failed to explain the question well enough. I thought I'd explained earlier.
successor: target ^(self select: [:each | target < each]) min is trivial. What's wrong with it is that it allocates an intermediate collection and takes two passes. FIXING that is is also trivial. successor: target ^(self virtualSelect: [:each | target < each]) min ^^^^^^^ This does allocate something, but it's just a few words, and a single traversal is one. In other languages/contexts we'd be talking about loop fusion/listless transformation/deforestation. It is my understanding that using Transducers would get me *this* level of improvement. The problem is that this is still a linear-time algorithm. If you take advantage of the order in a SortedCollection or SortedSet,it can take logarithmic time. When SortedSet is implemented as a splay tree -- as it is in my library -- iterating over all its elements using #successor: is amortised CONSTANT time per element. So we need THREE algorithms: - worst case O(n) select+min - worst case O(lg n) binary search - amortised O(1) splaying and we want the algorithm selection to be A U T O M A T I C. That's the point of using an object-oriented language. I say what I want done and the receiver decides how to do it. Anything where I have to write different calling code depending on the structure of the receiver doesn't count as a solution. Now we come to the heart of the problem. The binary search algorithm is NOT a special case of the linear search algorithm. It is not made of pieces that can be related to the parts of the linear search algorithm. The splaying algorithm is NOT a special case of the linear search algorithm OR the binary search algorithm. It is not made of pieces that can be related to their parts. So *IF* I want automatic selection of an appropriate algorithm, then I have to rely on inheritance and overriding, and in order to do that I have to have a named method that *can* be overridden, and at that point I'm no longer building a transducer out of pluggable pieces. So that's the point of this exercise. How do we get (a) composition of transducers out of pluggable parts AND (b) automatic selection of appropriate algorithms On Fri, 21 Apr 2023 at 20:35, Steffen Märcker <merk...@web.de> wrote: > Hi Richard, > > Now that's much clearer to me: > min{y | y in c . y > x} "strict supremum" > max{y | y in c . y < x} "strict infimum" > > For the general case of a sequence (not sorted) of elements we can do > > strictSupremumOf: x in: sequence > > ^(sequence transduce filter: [:y | y > x]) "virtual sequence" > inject: nil > into: [:min :b | min ifNotNil: [:a | a min: b]] > > I just picked a variant of minimum that answers nil if no element is > found. Other variants would work, too. > The focus of transducers is on re-use and composition of processing steps. > We can break this up into steps if needed: > > minimum := [:min :b | min ifNotNil: [:a | a min: b]] init: nil. > "reduction" > upperBounds := Filter predicate: [:y | y > x]. "transducer" > strictSup := minimum transduce: upperBounds. "transformed reduction" > ^strictSup reduce: sequence > > We can also use a different notation similar to a data flow: > > minimum <~ upperBounds <~ sequence > > Of course, if we know how the sequence is sorted, we should use another > algorithm. Assuming an ascending order with no random access, we'd change > minimum to stop early: > > minimum := [:min :b | Stop result: b]. > > Kind regards, > Steffen > > > Richard O'Keefe schrieb am Freitag, 21. April 2023 05:33:44 (+02:00): > > successor of x in c = the smallest element of c that is larger than x > min {y | y in c . y > x} > predecessor of x in c = the largest element of c that is smaller than x > max {y | y in c . y < x} > > On Thu, 20 Apr 2023 at 21:08, Steffen Märcker <merk...@web.de> wrote: > >> Dear Richard, >> >> thanks for that additional piece. I'll put insert-<left/right> on my list >> of possible variants. I think we come back to naming after the initial port >> is done and everyone can play with it. Generally, I made the observation to >> better be careful with names since it's too easy to alienate other or >> trigger wrong assumptions. >> >> New topic! (quote below) >> >> Honestly, my knowledge of Haskell is rather limited and rusted. Hence, I >> am having difficulties understanding what exactly these operations with a >> sequence of elements. Can you give an example or some pseude/smalltalk code >> from your use-case and library? >> >> Kind regards >> >> >> Changing the subject a wee bit, there's an operation family >> in my library, and I wonder how it would fit into Transducers? >> To avoid bias, here's a specification in Haskell (for lists, >> because I haven't had any luck installing Data.Witherable). >> >> uccessorBy, predecessorBy :: (a -> a -> Ordering) -> a -> [a] -> a >> successor, predecessor :: Ord a => a -> [a] -> a >> >> successor = successorBy compare >> >> successorBy cmp x = minimumBy cmp . filter (\y -> cmp x y == LT) >> >> predecessor = predecessorBy compare >> >> predecessorBy cmp = successorBy (flip cmp) >> >> The reason these operations exist is to pick neighbouring >> elements in SortedCollections and SortedSets. But they make >> *sense* for any Enumerable. So there are "generic" >> definitions with orderrides for those two classes. >> >> A filter + a reduce . Traditionally, a #select:thenFold:ifNone: >> in order to avoid building an intermediate collection. That much >> I see how to do with transducers. But you can't get the desired >> override for #successor:[sortBlock:][ifNone:] by overriding >> #select:thenFold:ifNone: in SortedCollection or SortedSet. So what >> *should* one do? >> >> > -- > Gesendet mit Vivaldi Mail. Laden Sie Vivaldi kostenlos unter vivaldi.com > herunter >