4

I am trying to use a property root as default parameter in add function as below and it is giving me an error:

Cannot use instance member 'root' as a default parameter .

However, when I am using root in a nested function search it is working fine.

class Trie {
   var root = TrieNode()

   func add(value: String, node: TrieNode? = root) { // error
      var myNode = node
      if myNode == nil {
        myNode = root
      }
      if value.count == 0 {
        node?.setEnd()
        return
      } else if myNode!.keys[String(value.first!)] == nil {
        myNode!.keys[String(value.first!)] = TrieNode()
        return add(value: String(value.dropFirst()), node: myNode!.keys[String(value.first!)])
    } else {
        return add(value: String(value.dropFirst()), node: myNode!.keys[String(value.first!)])
    }
   }


  func allWords() -> [String] {
    var words = [String]()
    func search(node: TrieNode = root, string: String) { // Here it is working fine.
        if node.keys.count != 0 {
            for key in node.keys.keys {
                search(node: node.keys[key]!, string: string+key)
            }
            if node.isEnd {
                words.append(string)
            }
        } else {
            if string.count > 0  {
                words.append(string)
            }
        }
    }

    search(node: self.root, string: "")
    return words
}
}

Can someone tell me why I am not able to use a property as the default parameter?

Ankur Arya
  • 4,693
  • 5
  • 29
  • 50
  • Looks like a duplicate of [Swift: using member constant as default value for function parameter](https://stackoverflow.com/q/29730452/1187415) to me. – Martin R Apr 05 '20 at 06:53
  • @MartinR: I agree, but I wanted to know how a property can be used as a default parameter in a nested function. – Ankur Arya Apr 05 '20 at 07:06
  • Does this answer your question? [Swift: using member constant as default value for function parameter](https://stackoverflow.com/questions/29730452/swift-using-member-constant-as-default-value-for-function-parameter) – Matias Jurfest Apr 05 '20 at 07:56

3 Answers3

6

The reason is in different context visibility, the arguments context of interface function is external in relation to class declaration, so members are not visible, but nested function is declared inside class context, as other function body, so members are visible.

So the solution might be static, as already proposed, but it might have drawbacks (eg. for reference default members), so I recommend to use it only for constants.

The other possible solutions is below

func add(value: String, node: TrieNode?) { // no error
    func _add(value: String, node: TrieNode? = root) { // in-context
        var myNode = node
        if myNode == nil {
            myNode = root
        }
        if value.count == 0 {
            node?.setEnd()
            return
        } else if myNode!.keys[String(value.first!)] == nil {
            myNode!.keys[String(value.first!)] = TrieNode()
            return add(value: String(value.dropFirst()), node: myNode!.keys[String(value.first!)])
        } else {
            return add(value: String(value.dropFirst()), node: myNode!.keys[String(value.first!)])
        }
    }
    _add(value: value, node: node)
}
Asperi
  • 228,894
  • 20
  • 464
  • 690
  • _the arguments context of interface function is external_ why do you say it's external? If this was a private property then I would have agreed, but root is a visible/public property. Why can't it get processed just like a function gets processed? – mfaani Nov 16 '20 at 20:04
4

Other answers have explained why you can't do it.

I suggest that the simplest way to achieve what you want is via the nil coalescing operator

func add(value: String, node: TrieNode? = nil) { // error
  var myNode = node ?? root

  guard let key = value.first, keyStr = String(key) else {
      myNode.setEnd()
      return
  }

  let updateNode = myNode.keys[keyStr, default: TrieNode()]
  return add(value: String(value.dropFirst(), node: updateNode)
 }
Paulw11
  • 108,386
  • 14
  • 159
  • 186
0

You could introduce a static variable as suggested in the comments and then update the value of it when root changes

class Trie {
    static var defaultNode = TrieNode()

    var root = TrieNode() {
        didSet {
            Trie.defaultNode = root
        }
    }

func add(value: String, node: TrieNode? = Trie.defaultNode) {

The main issue with this is that defaultNode would be the same for all instances of Trie and also that all instances can change it, of course you can let declare it so that it is constant but then what is the point of having it at all (although I don't see that you ever change root?).

Another option is to skip the default value in the function declaration and instead have it inside the function

class Trie {
    var root = TrieNode()

    func add(value: String, node: TrieNode?) { 
        let myNode = node ?? root
        if value.count == 0 {
            myNode.setEnd()
            return
        }
        //... rest of code
    }
Joakim Danielson
  • 43,251
  • 5
  • 22
  • 52