Next / Previous / Contents / Shipman's homepage

28.5. EqBind.__chainClosure(): Recursive lookup function

This function is very similar to Section 28.4, “EqBind.lookup(): What is the referenced taxon?”, except that it keeps track of which direct equivalent codes have been seen so far, so as to avoid an infinite loop.

nomcompile3
# - - -   E q B i n d . _ _ c h a i n C l o s u r e

    def __chainClosure ( self, seenAbbrs, sym ):
        '''Find a taxon at sym or what it refers to

          [ (seenAbbrs is a set of bird codes) and
            (sym is an AbSym instance) ->
              if sym is a direct equivalent that is in seenAbbrs
              or leads indirectly to a code in seenAbbrs ->
                raise ValueError
              else if sym is a direct equivalent that is not in
              seenAbbrs and leads indirectly to a taxon ->
                return that taxon
              else -> raise ValueError ]
          NB: In the last case, the chain leads to a CollBind
          or unbound symbol.
        '''

First we need to eliminate the case where sym does not even have a binding.

nomcompile3
        #-- 1 --
        # [ if sym is unbound ->
        #     raise ValueError
        #   else ->
        #     symBind = sym.binding
        symBind = sym.binding
        if symBind is None:
            raise ValueError ( "Code '%s' is referenced to code "
                "'%s', which is undefined." %
                (self.abbr, sym.abbr) )

If sym is another EqBind instance, set symAbbr to that instance's code. If symBind is not an EqBind instance, we can call its .lookup() method to see if the trail leads eventually to a taxon; if not, that method returns None.

nomcompile3
        #-- 2 --
        # [ if symBind is an EqBind ->
        #     symAbbr  :=  symBind.abbr
        #   else if symBind defines a taxon ->
        #     return that taxon as a Taxon instance
        #   else -> raise ValueError ]
        if isinstance(symBind, EqBind):
            symAbbr = symBind.abbr
        else:
            taxon = symBind.lookup()
            if taxon is None:
                raise ValueError ( "Code '%s' is not associated "
                    "with a taxon." % self.abbr )
            else:
                return taxon

At this point we know that sym is a direct equivalent. Check for an infinite loop.

nomcompile3
        #-- 3 --
        # [ if symAbbr is an element of seenAbbrs ->
        #     raise ValueError
        #   else -> 
        #     newSet  :=  union ( seenAbbrs, set([symAbbr]) ) ]
        if symAbbr in seenAbbrs:
            raise ValueError ( "Code '%s' leads to a circuit of "
                "references." % self.abbr )
        else:
            newSet = seenAbbrs.union ( set ( [symAbbr] ) )

Add the new bird code from sym to the set of codes we have seen, then call ourself recursively to chase the reference chain the rest of the way.

nomcompile3
        #-- 4 --
        # [ symBind is an EqBind -> 
        #     if symAbbr is directly equivalent to a code that is in
        #     union(seenAbbrs, set([symAbbr]) ->
        #       raise ValueError
        #     else if sym is a direct equivalent that is not in
        #     seenAbbrs and leads indirectly to a taxon ->
        #       return that taxon
        #     else -> raise ValueError ]
        return self.__chainClosure ( newSet, symBind.prefSym )