From f512f1144d0ee2a2f0e9a8aeda627d0e82326f58 Mon Sep 17 00:00:00 2001 From: Neverik Date: Fri, 5 Oct 2018 10:31:19 +0700 Subject: [PATCH 1/6] Tree traversal in smalltalk --- .../code/smalltalk/tree_traversal.st | 101 ++++++++++++++++++ contents/tree_traversal/tree_traversal.md | 14 +++ 2 files changed, 115 insertions(+) create mode 100644 contents/tree_traversal/code/smalltalk/tree_traversal.st diff --git a/contents/tree_traversal/code/smalltalk/tree_traversal.st b/contents/tree_traversal/code/smalltalk/tree_traversal.st new file mode 100644 index 000000000..e6b65d6c7 --- /dev/null +++ b/contents/tree_traversal/code/smalltalk/tree_traversal.st @@ -0,0 +1,101 @@ +Object subclass: #Node + instanceVariableNames: 'children data' + classVariableNames: '' + package: '' + +Node>>children + "Children getter." + ^ children + +Node>>children: newChildren + "Children setter." + children := newChildren. + +Node>>data + "Data getter" + ^ data + +Node>>data: newData + "Data setter" + data := newData. + +Node>>dfsRecursive + "Recursive depth first search." + data isNil ifFalse: [ + Transcript show: data. + Transcript cr. + ]. + children collect: [ :each | each dfsRecursive]. + +Node>>dfsRecursivePostorder + "Recursive depth first search (post-order)." + children collect: [ :each | each dfsRecursivePostorder ]. + data isNil ifTrue: [ ^ self ]. + Transcript show: data. + Transcript cr. + +Node>>dfsInOrderBinaryTree + "Recursive depth first search on a binary tree in order." + children size = 2 ifTrue: [ + (children at: 1) dfsInOrderBinaryTree. + Transcript show: data. + Transcript cr. + (children at: 2) dfsInOrderBinaryTree. + ^self. + ]. + children size = 1 ifTrue: [ + (children at: 1) dfsInOrderBinaryTree. + Transcript show: data. + Transcript cr. + ^self. + ]. + children size = 0 ifTrue: [ + Transcript show: data. + Transcript cr. + ^self. + ]. + Transcript show: 'This is not a binary tree!'. + Transcript cr. + children length + +Node>>dfsStack + "Depth-first search with a stack." + | stack top | + stack := Stack new. + stack push: self. + [stack size > 0] whileTrue: [ + top := stack pop. + Transcript show: top data. + Transcript cr. + top children reverseDo: [ :child | + stack push: child + ]. + ]. + +Node>>bfs + "A breadth-first tree search using queues." + | queue current | + queue := LinkedList with: self. + [ queue size > 0 ] whileTrue: [ + current := queue first. + queue removeFirst. + Transcript show: current. + Transcript cr. + current children collect: [ :child | + queue addLast: child + ]. + ] + +| test | +test := Node new: 1 children: { Node new: 2. + Node new: 3 children: { Node new: 4. + Node new: 5. } }. +test dfsRecursive. +Transcript cr. +test dfsRecursivePostorder. +Transcript cr. +test dfsInOrderBinaryTree. +Transcript cr. +test dfsStack. +Transcript cr. +test bfs. diff --git a/contents/tree_traversal/tree_traversal.md b/contents/tree_traversal/tree_traversal.md index b80d09544..96eaf7c1a 100644 --- a/contents/tree_traversal/tree_traversal.md +++ b/contents/tree_traversal/tree_traversal.md @@ -32,6 +32,8 @@ This has not been implemented in your chosen language, so here is the Julia code [import:3-27, lang:"php"](code/php/tree_traversal.php) {% sample lang="crystal" %} [import:1-5, lang:"crystal"](code/crystal/tree-traversal.cr) +{% sample lang="st" %} +[import:1-20, lang:"st"](code/smalltalk/tree_traversal.st) {% endmethod %} Because of this, the most straightforward way to traverse the tree might be recursive. This naturally leads us to the Depth-First Search (DFS) method: @@ -66,6 +68,8 @@ Because of this, the most straightforward way to traverse the tree might be recu [import:31-35, lang:"php"](code/php/tree_traversal.php) {% sample lang="crystal" %} [import:7-10, lang:"crystal"](code/crystal/tree-traversal.cr) +{% sample lang="st" %} +[import:22-28, lang:"st"](code/smalltalk/tree_traversal.st) {% endmethod %} At least to me, this makes a lot of sense. We fight recursion with recursion! First, we first output the node we are on and then we call `DFS_recursive(...)` on each of its children nodes. This method of tree traversal does what its name implies: it goes to the depths of the tree first before going through the rest of the branches. In this case, the ordering looks like: @@ -108,6 +112,8 @@ Now, in this case the first element searched through is still the root of the tr [import:37-41, lang:"php"](code/php/tree_traversal.php) {% sample lang="crystal" %} [import:12-15, lang:"crystal"](code/crystal/tree-traversal.cr) +{% sample lang="st" %} +[import:30-35, lang:"st"](code/smalltalk/tree_traversal.st) {% endmethod %}

@@ -145,6 +151,8 @@ In this case, the first node visited is at the bottom of the tree and moves up t [import:43-62, lang:"php"](code/php/tree_traversal.php) {% sample lang="crystal" %} [import:17-31, lang:"crystal"](code/crystal/tree-traversal.cr) +{% sample lang="st" %} +[import:37-59, lang:"st"](code/smalltalk/tree_traversal.st) {% endmethod %}

@@ -192,6 +200,8 @@ In code, it looks like this: [import:64-73, lang:"php"](code/php/tree_traversal.php) {% sample lang="crystal" %} [import:33-41, lang:"crystal"](code/crystal/tree-traversal.cr) +{% sample lang="st" %} +[import:61-73, lang:"st"](code/smalltalk/tree_traversal.st) {% endmethod %} All this said, there are a few details about DFS that might not be idea, depending on the situation. For example, if we use DFS on an incredibly long tree, we will spend a lot of time going further and further down a single branch without searching the rest of the data structure. In addition, it is not the natural way humans would order a tree if asked to number all the nodes from top to bottom. I would argue a more natural traversal order would look something like this: @@ -231,6 +241,8 @@ And this is exactly what Breadth-First Search (BFS) does! On top of that, it can [import:65-74, lang:"php"](code/php/tree_traversal.php) {% sample lang="crystal" %} [import:43-51, lang:"crystal"](code/crystal/tree-traversal.cr) +{% sample lang="st" %} +[import:75-87, lang:"st"](code/smalltalk/tree_traversal.st) {% endmethod %} ## Example Code @@ -272,6 +284,8 @@ The code snippets were taken from this [Scratch project](https://scratch.mit.edu [import, lang:"php"](code/php/tree_traversal.php) {% sample lang="crystal" %} [import, lang:"crystal"](code/crystal/tree-traversal.cr) +{% sample lang="st" %} +[import, lang:"st"](code/smalltalk/tree_traversal.st) {% endmethod %} From 0970e196184544e6ed4a4ca58bccc84da1950597 Mon Sep 17 00:00:00 2001 From: Stepan Date: Fri, 5 Oct 2018 11:03:56 +0700 Subject: [PATCH 2/6] Fixed indent --- .../code/smalltalk/tree_traversal.st | 112 +++++++++--------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/contents/tree_traversal/code/smalltalk/tree_traversal.st b/contents/tree_traversal/code/smalltalk/tree_traversal.st index e6b65d6c7..196b30862 100644 --- a/contents/tree_traversal/code/smalltalk/tree_traversal.st +++ b/contents/tree_traversal/code/smalltalk/tree_traversal.st @@ -1,69 +1,69 @@ Object subclass: #Node - instanceVariableNames: 'children data' - classVariableNames: '' - package: '' + instanceVariableNames: 'children data' + classVariableNames: '' + package: '' Node>>children - "Children getter." - ^ children + "Children getter." + ^ children Node>>children: newChildren - "Children setter." - children := newChildren. + "Children setter." + children := newChildren. Node>>data - "Data getter" - ^ data + "Data getter" + ^ data Node>>data: newData - "Data setter" - data := newData. + "Data setter" + data := newData. Node>>dfsRecursive - "Recursive depth first search." - data isNil ifFalse: [ - Transcript show: data. - Transcript cr. - ]. - children collect: [ :each | each dfsRecursive]. + "Recursive depth first search." + data isNil ifFalse: [ + Transcript show: data. + Transcript cr. + ]. + children collect: [ :each | each dfsRecursive]. Node>>dfsRecursivePostorder - "Recursive depth first search (post-order)." - children collect: [ :each | each dfsRecursivePostorder ]. - data isNil ifTrue: [ ^ self ]. - Transcript show: data. - Transcript cr. + "Recursive depth first search (post-order)." + children collect: [ :each | each dfsRecursivePostorder ]. + data isNil ifTrue: [ ^ self ]. + Transcript show: data. + Transcript cr. Node>>dfsInOrderBinaryTree - "Recursive depth first search on a binary tree in order." - children size = 2 ifTrue: [ - (children at: 1) dfsInOrderBinaryTree. - Transcript show: data. - Transcript cr. - (children at: 2) dfsInOrderBinaryTree. - ^self. - ]. - children size = 1 ifTrue: [ - (children at: 1) dfsInOrderBinaryTree. - Transcript show: data. - Transcript cr. - ^self. - ]. - children size = 0 ifTrue: [ - Transcript show: data. - Transcript cr. - ^self. - ]. - Transcript show: 'This is not a binary tree!'. + "Recursive depth first search on a binary tree in order." + children size = 2 ifTrue: [ + (children at: 1) dfsInOrderBinaryTree. + Transcript show: data. + Transcript cr. + (children at: 2) dfsInOrderBinaryTree. + ^self. + ]. + children size = 1 ifTrue: [ + (children at: 1) dfsInOrderBinaryTree. + Transcript show: data. Transcript cr. - children length + ^self. + ]. + children size = 0 ifTrue: [ + Transcript show: data. + Transcript cr. + ^self. + ]. + Transcript show: 'This is not a binary tree!'. + Transcript cr. + children length Node>>dfsStack "Depth-first search with a stack." | stack top | stack := Stack new. stack push: self. - [stack size > 0] whileTrue: [ + [stack size > 0] whileTrue: [ top := stack pop. Transcript show: top data. Transcript cr. @@ -73,18 +73,18 @@ Node>>dfsStack ]. Node>>bfs - "A breadth-first tree search using queues." - | queue current | - queue := LinkedList with: self. - [ queue size > 0 ] whileTrue: [ - current := queue first. - queue removeFirst. - Transcript show: current. - Transcript cr. - current children collect: [ :child | - queue addLast: child - ]. - ] + "A breadth-first tree search using queues." + | queue current | + queue := LinkedList with: self. + [ queue size > 0 ] whileTrue: [ + current := queue first. + queue removeFirst. + Transcript show: current. + Transcript cr. + current children collect: [ :child | + queue addLast: child + ]. + ] | test | test := Node new: 1 children: { Node new: 2. From 69d51185baa1517cfacfb3f7fa06dc1adc6405b4 Mon Sep 17 00:00:00 2001 From: Stepan Date: Fri, 5 Oct 2018 11:56:28 +0700 Subject: [PATCH 3/6] Kinda some changes --- .../code/smalltalk/tree_traversal.st | 91 +++++++++---------- 1 file changed, 43 insertions(+), 48 deletions(-) diff --git a/contents/tree_traversal/code/smalltalk/tree_traversal.st b/contents/tree_traversal/code/smalltalk/tree_traversal.st index 196b30862..2f0a5f0a9 100644 --- a/contents/tree_traversal/code/smalltalk/tree_traversal.st +++ b/contents/tree_traversal/code/smalltalk/tree_traversal.st @@ -19,83 +19,78 @@ Node>>data: newData "Data setter" data := newData. -Node>>dfsRecursive +Node>>dfsRecursive: value "Recursive depth first search." - data isNil ifFalse: [ - Transcript show: data. - Transcript cr. + data = value ifTrue: [ + ^true. ]. - children collect: [ :each | each dfsRecursive]. + children collect: [ :child | + (child dfsRecursive: value) ifTrue: [ ^true. ] + ] + ^false. -Node>>dfsRecursivePostorder +Node>>dfsRecursivePostOrder: value "Recursive depth first search (post-order)." - children collect: [ :each | each dfsRecursivePostorder ]. - data isNil ifTrue: [ ^ self ]. - Transcript show: data. - Transcript cr. - -Node>>dfsInOrderBinaryTree + children collect: [ :child | + (child dfsRecursive: value) ifTrue: [ ^true. ] + ]. + ^(data = value). + +Node>>dfsInOrderBinaryTree: value "Recursive depth first search on a binary tree in order." - children size = 2 ifTrue: [ - (children at: 1) dfsInOrderBinaryTree. - Transcript show: data. - Transcript cr. - (children at: 2) dfsInOrderBinaryTree. + children size > 2 ifTrue: [ + Transcript show: 'This is not a binary tree!'; cr. ^self. ]. - children size = 1 ifTrue: [ - (children at: 1) dfsInOrderBinaryTree. - Transcript show: data. - Transcript cr. - ^self. - ]. - children size = 0 ifTrue: [ - Transcript show: data. - Transcript cr. - ^self. - ]. - Transcript show: 'This is not a binary tree!'. - Transcript cr. - children length + children size = 2 ifTrue: [ + (children at: 1) dfsInOrderBinaryTree: value. + ] + data = value ifTrue: [ + Transcript show: data; cr. + ] + children size >= 1 ifTrue: [ + (children at: 0) dfsInOrderBinaryTree: value. + ] + ^self. -Node>>dfsStack +Node>>dfsStack: value "Depth-first search with a stack." | stack top | stack := Stack new. stack push: self. [stack size > 0] whileTrue: [ top := stack pop. - Transcript show: top data. - Transcript cr. + top data = value ifTrue: [ + ^true. + ]. top children reverseDo: [ :child | - stack push: child + stack push: child. ]. ]. + ^false -Node>>bfs +Node>>bfs: value "A breadth-first tree search using queues." | queue current | queue := LinkedList with: self. [ queue size > 0 ] whileTrue: [ current := queue first. queue removeFirst. - Transcript show: current. - Transcript cr. + current data = value ifTrue: [ + ^true. + ] current children collect: [ :child | queue addLast: child ]. - ] + ]. + ^false | test | test := Node new: 1 children: { Node new: 2. Node new: 3 children: { Node new: 4. Node new: 5. } }. -test dfsRecursive. -Transcript cr. -test dfsRecursivePostorder. -Transcript cr. -test dfsInOrderBinaryTree. -Transcript cr. -test dfsStack. -Transcript cr. -test bfs. +Transcript show: (test dfsRecursive: 6); cr. +Transcript show: (test dfsRecursivePostorder: 4); cr. +test dfsInOrderBinaryTree: 7. +Transcript show: (test dfsStack: 2); cr. +Transcript show: (test bfs: 4); cr. From c0a07b24c9d66b8f1618f0bb2e11db93b8a8e789 Mon Sep 17 00:00:00 2001 From: Stepan Date: Fri, 5 Oct 2018 12:07:05 +0700 Subject: [PATCH 4/6] Shortening text --- contents/tree_traversal/tree_traversal.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/contents/tree_traversal/tree_traversal.md b/contents/tree_traversal/tree_traversal.md index 96eaf7c1a..61e96f1b4 100644 --- a/contents/tree_traversal/tree_traversal.md +++ b/contents/tree_traversal/tree_traversal.md @@ -33,7 +33,7 @@ This has not been implemented in your chosen language, so here is the Julia code {% sample lang="crystal" %} [import:1-5, lang:"crystal"](code/crystal/tree-traversal.cr) {% sample lang="st" %} -[import:1-20, lang:"st"](code/smalltalk/tree_traversal.st) +[import:1-20, lang:"smalltalk"](code/smalltalk/tree_traversal.st) {% endmethod %} Because of this, the most straightforward way to traverse the tree might be recursive. This naturally leads us to the Depth-First Search (DFS) method: @@ -69,7 +69,7 @@ Because of this, the most straightforward way to traverse the tree might be recu {% sample lang="crystal" %} [import:7-10, lang:"crystal"](code/crystal/tree-traversal.cr) {% sample lang="st" %} -[import:22-28, lang:"st"](code/smalltalk/tree_traversal.st) +[import:22-27, lang:"smalltalk"](code/smalltalk/tree_traversal.st) {% endmethod %} At least to me, this makes a lot of sense. We fight recursion with recursion! First, we first output the node we are on and then we call `DFS_recursive(...)` on each of its children nodes. This method of tree traversal does what its name implies: it goes to the depths of the tree first before going through the rest of the branches. In this case, the ordering looks like: @@ -113,7 +113,7 @@ Now, in this case the first element searched through is still the root of the tr {% sample lang="crystal" %} [import:12-15, lang:"crystal"](code/crystal/tree-traversal.cr) {% sample lang="st" %} -[import:30-35, lang:"st"](code/smalltalk/tree_traversal.st) +[import:29-34, lang:"smalltalk"](code/smalltalk/tree_traversal.st) {% endmethod %}

@@ -152,7 +152,7 @@ In this case, the first node visited is at the bottom of the tree and moves up t {% sample lang="crystal" %} [import:17-31, lang:"crystal"](code/crystal/tree-traversal.cr) {% sample lang="st" %} -[import:37-59, lang:"st"](code/smalltalk/tree_traversal.st) +[import:36-49, lang:"smalltalk"](code/smalltalk/tree_traversal.st) {% endmethod %}

@@ -201,7 +201,7 @@ In code, it looks like this: {% sample lang="crystal" %} [import:33-41, lang:"crystal"](code/crystal/tree-traversal.cr) {% sample lang="st" %} -[import:61-73, lang:"st"](code/smalltalk/tree_traversal.st) +[import:47-58, lang:"smalltalk"](code/smalltalk/tree_traversal.st) {% endmethod %} All this said, there are a few details about DFS that might not be idea, depending on the situation. For example, if we use DFS on an incredibly long tree, we will spend a lot of time going further and further down a single branch without searching the rest of the data structure. In addition, it is not the natural way humans would order a tree if asked to number all the nodes from top to bottom. I would argue a more natural traversal order would look something like this: @@ -242,7 +242,7 @@ And this is exactly what Breadth-First Search (BFS) does! On top of that, it can {% sample lang="crystal" %} [import:43-51, lang:"crystal"](code/crystal/tree-traversal.cr) {% sample lang="st" %} -[import:75-87, lang:"st"](code/smalltalk/tree_traversal.st) +[import:60-71, lang:"smalltalk"](code/smalltalk/tree_traversal.st) {% endmethod %} ## Example Code @@ -285,7 +285,7 @@ The code snippets were taken from this [Scratch project](https://scratch.mit.edu {% sample lang="crystal" %} [import, lang:"crystal"](code/crystal/tree-traversal.cr) {% sample lang="st" %} -[import, lang:"st"](code/smalltalk/tree_traversal.st) +[import, lang:"smalltalk"](code/smalltalk/tree_traversal.st) {% endmethod %} From 3395e0a28e14dbcd5369802b05ca89758aecb44b Mon Sep 17 00:00:00 2001 From: Stepan Date: Fri, 5 Oct 2018 12:07:13 +0700 Subject: [PATCH 5/6] Shortening code --- .../code/smalltalk/tree_traversal.st | 55 +++++++------------ 1 file changed, 20 insertions(+), 35 deletions(-) diff --git a/contents/tree_traversal/code/smalltalk/tree_traversal.st b/contents/tree_traversal/code/smalltalk/tree_traversal.st index 2f0a5f0a9..09969b323 100644 --- a/contents/tree_traversal/code/smalltalk/tree_traversal.st +++ b/contents/tree_traversal/code/smalltalk/tree_traversal.st @@ -19,24 +19,17 @@ Node>>data: newData "Data setter" data := newData. -Node>>dfsRecursive: value +Node>>dfsRecursive "Recursive depth first search." - data = value ifTrue: [ - ^true. - ]. - children collect: [ :child | - (child dfsRecursive: value) ifTrue: [ ^true. ] - ] - ^false. + Transcript show: data; cr. + children collect: [ :child | child dfsRecursive ] -Node>>dfsRecursivePostOrder: value +Node>>dfsRecursivePostOrder "Recursive depth first search (post-order)." - children collect: [ :child | - (child dfsRecursive: value) ifTrue: [ ^true. ] - ]. - ^(data = value). + children collect: [ :child | (child dfsRecursivePostOrder)]. + Transcript show: data; cr. -Node>>dfsInOrderBinaryTree: value +Node>>dfsInOrderBinaryTree "Recursive depth first search on a binary tree in order." children size > 2 ifTrue: [ Transcript show: 'This is not a binary tree!'; cr. @@ -44,53 +37,45 @@ Node>>dfsInOrderBinaryTree: value ]. children size = 2 ifTrue: [ (children at: 1) dfsInOrderBinaryTree: value. - ] - data = value ifTrue: [ - Transcript show: data; cr. - ] + ]. + Transcript show: data; cr. children size >= 1 ifTrue: [ (children at: 0) dfsInOrderBinaryTree: value. - ] + ]. ^self. -Node>>dfsStack: value +Node>>dfsStack "Depth-first search with a stack." | stack top | stack := Stack new. stack push: self. - [stack size > 0] whileTrue: [ + [stack size > 0] whileTrue: [ top := stack pop. - top data = value ifTrue: [ - ^true. - ]. + Transcript show: (top data); cr. top children reverseDo: [ :child | stack push: child. ]. ]. - ^false -Node>>bfs: value +Node>>bfs "A breadth-first tree search using queues." | queue current | queue := LinkedList with: self. [ queue size > 0 ] whileTrue: [ current := queue first. queue removeFirst. - current data = value ifTrue: [ - ^true. - ] + Transcript show: (current data); cr. current children collect: [ :child | queue addLast: child ]. ]. - ^false | test | test := Node new: 1 children: { Node new: 2. Node new: 3 children: { Node new: 4. Node new: 5. } }. -Transcript show: (test dfsRecursive: 6); cr. -Transcript show: (test dfsRecursivePostorder: 4); cr. -test dfsInOrderBinaryTree: 7. -Transcript show: (test dfsStack: 2); cr. -Transcript show: (test bfs: 4); cr. +test dfsRecursive. +test dfsRecursivePostorder. +test dfsInOrderBinaryTree. +test dfsStack: 2. +test bfs: 4); cr. From 8e3300b2d368d3fe2512b898edbc2489a9a58671 Mon Sep 17 00:00:00 2001 From: Stepan Date: Fri, 5 Oct 2018 12:09:16 +0700 Subject: [PATCH 6/6] typo fix --- contents/tree_traversal/code/smalltalk/tree_traversal.st | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contents/tree_traversal/code/smalltalk/tree_traversal.st b/contents/tree_traversal/code/smalltalk/tree_traversal.st index 09969b323..411e5ff45 100644 --- a/contents/tree_traversal/code/smalltalk/tree_traversal.st +++ b/contents/tree_traversal/code/smalltalk/tree_traversal.st @@ -77,5 +77,5 @@ test := Node new: 1 children: { Node new: 2. test dfsRecursive. test dfsRecursivePostorder. test dfsInOrderBinaryTree. -test dfsStack: 2. -test bfs: 4); cr. +test dfsStack. +test bfs.