a recursive function is one that calls itself, not one that loops per se
I often find that it's easier to implement "recursion" in a loop of some sort though. Maybe what I think of as "recursion" isn't technically recursion... I'm not a formally trained programmer by any means, so appologies if what I say is utter nonsense!That said - Here's a practical ArcGIS/Python example of something I would consider recursion. This code assigns a unique identifier (a "cluster ID" if you will) to features that within a given distance of each other (basically a "select by location" that keeps executing as long as the selected set grows. Once there are no more features to select, then it tries to select another seed feature (that hasn't already been assigned a CLUSTER_ID value) in which to "grow" the next cluster from. It uses while loops and not function calls, although I supose it could be re-written to do that. import sys, string, os, arcpy
inLayer = arcpy.GetParameterAsText(0) #input layer
clusterIdField = arcpy.GetParameterAsText(1) #the name of the 'CLUSTER_ID' field that will be added to inLayer
selectionMethod = arcpy.GetParameterAsText(2) #the selectbylocation method (such as WITHIN_A_DISTANCE or INTERSECT)
distThreshold = arcpy.GetParameterAsText(3) #A distance threashold if using the WITHIN_A_DISTANCE selection method
selectionBuffer = arcpy.GetParameterAsText(4) #A max number of features to be selected set at once... performace gets slow if selection gets too big
if selectionBuffer in ("","#",None):
selectionBuffer = 500
if int(selectionBuffer) < 1:
selectionBuffer = 500
fieldList = arcpy.ListFields(inLayer, clusterIdField)
if fieldList == []:
arcpy.AddField_management(inLayer, clusterIdField, "LONG")
else:
if fieldList[0].type not in ["SmallInteger","Integer","Double","Float"]:
arcpy.AddError("ERROR: Existing " + str(clusterIdField) + " field type must be a numeric type (not a type of " + str(fieldList[0].type) + "! Exiting script..."); sys.exit(1)
oidFieldName = arcpy.Describe(inLayer).oidFieldName
arcpy.AddMessage("Cataloging " + str(oidFieldName) + " values...")
oidDict = {}
for r in arcpy.da.SearchCursor(inLayer, ["OID@"]):
oidDict[r[0]] = -1
featureCount = len(oidDict)
if featureCount == 0:
arcpy.AddError("ERROR: Could not find any features to identify! Exiting script...");sys.exit(1)
arcpy.AddMessage("Looking for clusters...")
identifiedFeatures = 0
clusterId = 0
arcpy.MakeFeatureLayer_management(inLayer, "fl1", "") #Test to see if this helps clear the memory leak
arcpy.SetProgressor("step", "Progress", 0, 100, 1)
pctDone = 0
while identifiedFeatures < featureCount:
clusterId = clusterId + 1
try:
nextOidSeedValue = (key for key,value in oidDict.items() if value == -1).next()
except:
break
arcpy.SelectLayerByAttribute_management("fl1", "NEW_SELECTION", oidFieldName + " = " + str(nextOidSeedValue))
globalPreSelectionOidSet = set()
globalPostSelectionOidSet = set([nextOidSeedValue])
loopCount = 0 #for fun
bufferReductionCount = 0 #for fun
while len(globalPostSelectionOidSet) > len(globalPreSelectionOidSet):
loopCount = loopCount + 1
preSelectionOidSet = set([r[0] for r in arcpy.da.SearchCursor("fl1", ["OID@"])])
globalPreSelectionOidSet = globalPreSelectionOidSet.union(preSelectionOidSet)
arcpy.SelectLayerByLocation_management("fl1", selectionMethod, "fl1", distThreshold, "NEW_SELECTION")
postSelectionOidSet = set([r[0] for r in arcpy.da.SearchCursor("fl1", ["OID@"])])
globalPostSelectionOidSet = globalPostSelectionOidSet.union(postSelectionOidSet)
if len(postSelectionOidSet) > int(selectionBuffer):
recentSelectionOidList = list(globalPostSelectionOidSet.difference(globalPreSelectionOidSet))
recentSelectionOidList.sort()
if len(recentSelectionOidList) > 0:
bufferReductionCount = bufferReductionCount + 1
arcpy.SelectLayerByAttribute_management("fl1", "NEW_SELECTION", oidFieldName + " in (" + ",".join(str(i) for i in recentSelectionOidList) + ")")
for oidValue in globalPostSelectionOidSet:
identifiedFeatures = identifiedFeatures + 1
oidDict[oidValue] = clusterId
pctDoneUpdate = int(identifiedFeatures/float(featureCount) * 100)
pctDoneDiff = pctDoneUpdate - pctDone
if pctDoneDiff >= 1:
for i in range(0,pctDoneDiff):
arcpy.SetProgressorPosition()
pctDone = pctDoneUpdate
arcpy.AddMessage("Updating attribute table...")
updateRows = arcpy.da.UpdateCursor(inLayer, ["OID@",clusterIdField])
for updateRow in updateRows:
updateRow[1] = oidDict[updateRow[0]]
updateRows.updateRow(updateRow)
del updateRow, updateRows
arcpy.AddMessage("Identified " + str(clusterId) + " cluster(s) for " + str(identifiedFeatures) + " features")