-2

If we program in oop way, we're provided flexibility for self.var

class Solution:
    def isValidBST(self, root):
        self.lastVal = - 2**32 
        self.isBST = True
        self.valid(root)
        return self.isBST

    def valid(self, node): # in-order traversal
        if node is None:
            return

        self.valid(node.left)
        if self.lastVal >= node.val:
            self.isBST = None
            return
        self.lastVal = node.val
        self.valid(node.right)

But if we program in functional way:

def isBST(root):
   is_bst = True
   last_val = -2 ** 32

  def valid(node):
    if node == None:
        return 
    valid(node.left)

    if node.val <= last_val:
        is_bst = False
        return
    last_val = node.val
    valid(node.right)

  valid(root)
  return is_bst

we will meet the issue:

local variable 'last_val' referenced before assignment.

Is there any way we can leverage self.var's flexibility in python functional programming?

Pythoner
  • 5,265
  • 5
  • 33
  • 49

2 Answers2

1

You can do without the Solution class, but the tree data structure will still need to exist. Considering you don't create it in the code you're given, I assume it already does.

So here's a more functional solution (in that its return value is what's important, and it doesn't have any external state):

def isBST(node, minVal=-2**32, maxVal=2**32):
    if node is None:
        # recursive base case.
        return True
    # ensure that this node has an appropriate value
    if node.val <= minVal or node.val > maxVal:
        return False
    # ensure that this node's children have appropriate values
    return isBST(node.left, minVal, node.val) and isBST(node.right, node.val, maxVal)

(as a note, I cribbed this algorithm from wikipedia because I was too lazy to figure it out myself).

You can't really implement this properly without including minVal and maxVal as parameters - but we can set default values for them when we define the function, and as a result you can still do isBST(root) and it'll work perfectly fine. And it's okay in functional programming for a function to have multiple inputs like this.

Green Cloak Guy
  • 23,793
  • 4
  • 33
  • 53
  • You seem to write an other program, not reuse the Solution code. – Pythoner Jun 14 '19 at 21:30
  • 1
    @Pythoner By `Solution` do you mean that leetcode forces you to wrap your function in that silly class? This is the problem. In functional programming you would not use a class like this! You might have a module, sure, but the idea of a useless wrapper class like `Solution` might only exists because leetcode wants to accept Java, and Java programmers can't do without these silly classes. I'm just guessing here, though. What is wrong with Green Cloak Guy's answer, exactly? – Ray Toal Jun 14 '19 at 21:43
1

Instance variables are like a constrained form of a global variable. Each instance can be seen as a self-contained (pun intended) scope that gets passed as an explicit function argument. Without a class, you would just pass the each shared variable explicitly, rather than implicitly as part of the self argument.

def isValidBST(root, lastVal, isBST):
    lastVal = - 2**32 
    self.isBST = True
    lastVal, isBST = valid(root, lastVal, isBST)
    return lastVal, isBST

def valid(node, lastVal, isBST):
    if node is None:
        return lastVal, isBST

    lastVal, isBST = valid(node.left, lastVal, isBST)
    if lastVal >= node.val:
        isBST = None
        return lastVal, isBST

    lastVal = node.val
    lastVal, isBST = valid(node.right, lastVal, isBST)
    return lastVal, isBST

Clearly, there is room for simplification in the above: isValidBST doesn't really need to take extra arguments, since it ignores them and sets them explicitly; you can simply use return valid(root, lastVal, isBST) instead of unpacking its return value and repacking to return the values; etc. But the idea is there: global variables can be replaced by explicit function arguments and returning values.


If you study a language like Haskell, you'll discover the State monad, which is a way of abstracting away the repetitiveness of passing global state around in this fashion and writing code that "looks" like it uses global variables.

chepner
  • 497,756
  • 71
  • 530
  • 681