Code archives/Algorithms/Generic Enumerator

This code has been declared by its author to be Public Domain code.

Download source code

Generic Enumerator by N2009
This is inspired by Warpy's code here: http://www.blitzbasic.com/codearcs/codearcs.php?code=2496

It's sort of the same idea, except that you can iterate over strings, and it should ideally support multidimensional arrays, strings (iterates over each character of the string), and it should be faster when iterating over the contents of TLists, TMaps, etc. since it's not going through the reflection module when calling the methods for their own enumerators.

This was mainly written so I could have an Inject method along the same lines as Ruby's Enumerable#inject. Works pretty well, I think, so I figured I'd share this bit of code, at least.

To use this in Lua with LuGI, just use this function on the TGenericEnumerator object returned by Enum():Init(obj):
function Iterator(obj)
	local function _iter_next(enum, var)
		if not enum then
			return nil
		end
		local n = enum:HasNext()
		if (type(n) == "number" and n < .5) or not n then
			return nil
		end
		return enum:NextObject()
	end
	
	local succ, enumobj = pcall(function (t) return t:ObjectEnumerator() end, obj)
	
	return _iter_next, enumobj, nil
end


This will work like so:
local enum = Enum():Init(list)

for value in Iterator(enum) do
	if type(value) == "userdata" then
		print(value:ToString())
	else
		print(value)
	end
end
SuperStrict

Import brl.Reflection

'buildopt:threads
'buildopt:clean

Public

' Creates a generic enumerator and returns it
' This is pretty much the only function that matters
Function EnumForObject:TGenericEnumerator(obj:Object)
	Return New TGenericEnumerator.InitWithObject(obj)
End Function

' BMax: EnumForObject(obj)  OR  New TGenericEnumerator.Init(obj)
' Lua: Enum():Init(obj)
Type TGenericEnumerator {expose category="cower.generic.iterator" constructor="Enum"}
	Method HasNext:Int() {bool}
		Throw "Not implemented in TGenericEnumerator"
	End Method
	Method NextObject:Object()
		Throw "Not implemented in TGenericEnumerator"
	End Method
	
	Method InitWithObject:TGenericEnumerator(obj:Object) {rename="Init"}
		Assert obj Else "Object is Null"
		Local tid:TTypeID = TTypeID.ForObject(obj)
		Select tid._class
			Case ArrayTypeId._class
				Return New TArrayEnumerator.InitWithObjectAndTypeId(obj, tid)
			Case StringTypeId._class
				Return New TStringEnumerator.InitWithObject(obj)
			Default
				Local enum:TGenericEnumerator = TGenericEnumerator(obj)
				If enum Then
					Return enum
				ElseIf tid.ExtendsType(ObjectTypeId) Then
					If tid.FindMethod("HasNext") And tid.FindMethod("NextObject") Then
						Return New TObjectEnumerator.InitWithEnumeratorAndTypeId(obj, tid)
					Else
						Return New TObjectEnumerator.InitWithObjectAndTypeId(obj, tid)
					EndIf
				EndIf
		End Select
		Throw "Object is not enumerable"
	End Method
	
	Method ObjectEnumerator:TGenericEnumerator()
		Return Self
	End Method
End Type

Private

Type TObjectEnumerator Extends TGenericEnumerator
	Field _enum:Object
	Field _enumNextObject:Object(enum:Object)
	Field _enumHasNext:Int(enum:Object)
	
	Method InitWithEnumeratorAndTypeId:TObjectEnumerator(obj:Object, typeid:TTypeId)
		Assert obj Else "Enumerator is null"
		
		_enum = obj
		
		Local meth:TMethod = typeid.FindMethod("NextObject")
		Assert meth Else "Enumerator does not implement NextObject"
		
		If meth._index < 65536 Then
			_enumNextObject = Byte Ptr Ptr(typeid._class+meth._index)[0]
		Else
			_enumNextObject = Byte Ptr(meth._index)
		EndIf
		
		meth = typeid.FindMethod("HasNext")
		Assert meth Else "Enumerator does not implement HasNext"
		
		If meth._index < 65536 Then
			_enumHasNext = Byte Ptr Ptr(typeid._class+meth._index)[0]
		Else
			_enumHasNext = Byte Ptr(meth._index)
		EndIf
		
		Return Self
	End Method
	
	Method InitWithObjectAndTypeId:TObjectEnumerator(obj:Object, typeid:TTypeId)
		' Get the enumerator object
		Local objenum:TMethod = typeid.FindMethod("ObjectEnumerator")
		Assert objenum Else "Object is not enumerable"
		Local enumObject:Object = objenum.Invoke(obj, New Object[0])
		
		' Get enumerator methods
		typeid = TTypeID.ForObject(enumObject)
		
		Return InitWithEnumeratorAndTypeId(enumObject, typeid)
	End Method
	
	Method HasNext:Int()
		Return _enumHasNext(_enum)
	End Method
	
	Method NextObject:Object()
		Return _enumNextObject(_enum)
	End Method
End Type

Type TArrayEnumerator Extends TGenericEnumerator
	Field _typeid:TTypeID
	Field _object:Object
	Field _idx:Int, _length:Int
	
	Method InitWithObjectAndTypeId:TArrayEnumerator(obj:Object, typeid:TTypeId)
		_object = obj
		_typeid = typeid
		_idx = 0
		_length = _typeid.ArrayLength(_object, 0)
		Return Self
	End Method
	
	Method HasNext:Int()
		While _idx < _length And Not _typeid.GetArrayElement(_object, _idx)
			_idx :+ 1
		Wend
		Return _idx < _length
	End Method
	
	Method NextObject:Object()
		Local val:Object = _typeid.GetArrayElement(_object, _idx)
		_idx :+ 1
		Return val
	End Method
End Type

Type TStringEnumerator Extends TGenericEnumerator
	Field _string:String
	Field _idx:Int

	Method InitWithObject:TStringEnumerator(obj:Object)
		_string = String(obj)
		_idx = 0
		Return Self
	End Method

	Method HasNext:Int()
		Return _idx < _string.Length
	End Method

	Method NextObject:Object()
		Local nidx:Int = _idx + 1
		Local val:Object = _string[_idx .. nidx]
		_idx = nidx
		Return val
	End Method
End Type

Comments

N2009
Added LuGI metadata and an example of using the enumerator in a for-in loop in Lua.


Code Archives Forum