TE
TechEcho
Home24h TopNewestBestAskShowJobs
GitHubTwitter
Home

TechEcho

A tech news platform built with Next.js, providing global tech news and discussions.

GitHubTwitter

Home

HomeNewestBestAskShowJobs

Resources

HackerNews APIOriginal HackerNewsNext.js

© 2025 TechEcho. All rights reserved.

Decision Table Patterns

112 pointsby MindGodsover 4 years ago

8 comments

ajucover 4 years ago
Logic tables are a secret weapon when refactoring spaghetti code.<p>You take all the conditions relevant to the part you&#x27;re refactoring.<p>You refactor common parts of the conditions (so if one if has (x&gt;3 &amp; y&lt;2) and another if has (x&gt;3 and y&gt;10) you get (x&gt;3), (y&lt;2), (y&gt;10).<p>You put that into a table, write all possible values that can happen (sounds like combinatoric explosion but it&#x27;s usually not, because many of them depend on each other and lots of combinations cannot happen or aren&#x27;t relevant to the result) - then rewrite the whole thing looking at the table.<p><pre><code> x&gt;3 | y&lt;2 | y&gt;10 | effect F | F | F | ... T | F | F | ... F | T | F | ... T | T | F | ... F | F | T | ... T | F | T | ... </code></pre> Another form is<p><pre><code> x | y | effect &lt;3 | &lt;2 | ... &gt;=3 | &lt;2 | ... &lt;3 | 2...10 | ... &gt;=3 | 2...10 | ... &lt;3 | &gt;10 | ... &gt;=3 | &gt;10 | ... </code></pre> Usually if the code is old enough there&#x27;s redundant branches, checking conditions that can&#x27;t happen, duplicated code &quot;just in case&quot;, etc.<p>With the table you can look at it and see input-output and refactor it without worrying about changing behaviour.<p>For simple cases it&#x27;s overkill, but with big enough mess it&#x27;s a life-saver.
评论 #24442932 未加载
评论 #24444951 未加载
aszenover 4 years ago
Decision table&#x27;s are really good at representing complex rules, in a language with pattern matching u can convert a nested if else block to a decision table that is much more readable, as an example see <a href="https:&#x2F;&#x2F;github.com&#x2F;finos&#x2F;morphir-examples&#x2F;blob&#x2F;master&#x2F;src&#x2F;Morphir&#x2F;Sample&#x2F;Rules&#x2F;Direct.elm" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;finos&#x2F;morphir-examples&#x2F;blob&#x2F;master&#x2F;src&#x2F;Mo...</a>
评论 #24439844 未加载
jdsalaroover 4 years ago
This article gave me a fair bit of nostalgic feelings. So much so, that I had to go and dig out one of my first university assignments from seven years ago[!], writing a simple Linux Shell, where I actually <i>had</i> to use &quot;decision tables&quot;. In my case I remember thinking about using an automata for a grammar to prevent my code from becoming an unmaintainable, unexplainable, SEGFAULT-y mess.<p>Most important of all, I remember the lecture assistant telling us that we &quot;won&#x27;t pass the assignment as long as I can bring your program to SEGFAULT.&quot;<p>Many of us struggled, SEGFAULT after SEGFAULT, with uneven quotes, pipes inside or outside quotes, double bars, double ampersands, etc. As the days progressed, many code-bases became more complex and more fragile as we tried to deal with the pesky inputs of the lecture assistant. When our turn came up, we all grinned while the lecture assistant tried in frustration to bring our shell to SEGFAULT. I remember pointing out &quot;those inputs are not supported by the grammar, and walking him through the bits I list below&quot;. Those were the days, oh man :)<p>---<p><pre><code> struct listNode { char *token; int type; struct listNode *next; }; ... #define M_BEGIN 0 #define T_WORD 1 #define T_QUOTE 2 #define T_BAR 3 #define T_AMP 4 #define T_NL 5 ... int validateInput(struct listNode * head) { unsigned char smpshGrammar[6][6] = { {0,1,0,0,0,1}, &#x2F;&#x2F; M_BEGIN {0,1,1,1,1,1}, &#x2F;&#x2F; T_WORD {0,1,1,1,1,1}, &#x2F;&#x2F; T_QUOTE {0,1,0,0,0,0}, &#x2F;&#x2F; T_BAR {0,0,0,0,0,1}, &#x2F;&#x2F; T_AMP {0,0,0,0,0,0}}; &#x2F;&#x2F; T_NL struct listNode * iterator = head; int validInput = 1; int previousState = 0; while(iterator &amp;&amp; validInput){ if(DEBUG) printf(&quot; Debug Information &gt; Validating transition from Q%d to Q%d ... Possible:%d\n&quot;, previousState, iterator-&gt;type, smpshGrammar[previousState][iterator-&gt;type]); if(smpshGrammar[previousState][iterator-&gt;type]) { previousState = iterator-&gt;type; iterator = iterator-&gt;next; } else { validInput = 0; } } if(previousState!=5){ validInput = 0; } return validInput; }</code></pre>
评论 #24440143 未加载
评论 #24441124 未加载
afandianover 4 years ago
This reminded me of Karnaugh Maps.<p><a href="https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Karnaugh_map" rel="nofollow">https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Karnaugh_map</a>
评论 #24441875 未加载
评论 #24442941 未加载
cdirkxover 4 years ago
A few years ago I was working with JetBrain&#x27;s MPS [1] and got excited about it&#x27;s projectional editor, which allows for different &quot;projections&quot; of your code, not just linear text. One of the things this enables is using descision tables directly within your code [2], as well as a bunch of other things like math notation or graphs for statemachines [3]. Being able to use another notation than just plaintext can make some code a lot clearer.<p>I know this isn&#x27;t going to replace code as text anytime soon, but it looked really interesting. Does anyone know of other projects experimenting with representing code in other ways?<p>[1] <a href="https:&#x2F;&#x2F;www.jetbrains.com&#x2F;mps&#x2F;" rel="nofollow">https:&#x2F;&#x2F;www.jetbrains.com&#x2F;mps&#x2F;</a><p>[2] <a href="https:&#x2F;&#x2F;www.jetbrains.com&#x2F;mps&#x2F;img&#x2F;screenshots&#x2F;2017.2&#x2F;domain-specific-languages.png" rel="nofollow">https:&#x2F;&#x2F;www.jetbrains.com&#x2F;mps&#x2F;img&#x2F;screenshots&#x2F;2017.2&#x2F;domain-...</a><p>[3] <a href="http:&#x2F;&#x2F;www.mbeddr.com&#x2F;platform" rel="nofollow">http:&#x2F;&#x2F;www.mbeddr.com&#x2F;platform</a>
评论 #24446806 未加载
dragontamerover 4 years ago
Oh, and another thing, so I&#x27;m making another comment.<p>&gt; Input Elimination: Often by using - we find that a column is unnecessary<p>I&#x27;ve found this to be true in many cases. However, efficiently calculating a potential &quot;input elimination&quot; has eluded me.<p>I&#x27;ve done research into various relational-database techniques. I&#x27;ve looked at binary decision diagrams (or their relational multi-valued counterparts: Multi-Valued Decision Diagrams). Etc. etc.<p>It is &quot;obvious&quot; that minimizing columns and removing unnecessary columns can grossly improve performance. But I can&#x27;t find any efficient algorithm that detects this case.<p>Lets say we have Table ABCD (with 4 columns: ABCD). To test if Column C can be eliminated, I came up with:<p><pre><code> ABD = (Select a, b, d from TableABCD where C = true) ABD2 = (Select a, b, d from TableABCD where C = false) If ABD == ABD2, then C is redundant. </code></pre> That&#x27;s for binary variables. If C has multiple values, then you have to do this for every value C could have.<p>-------<p>This seems inefficient to me. Especially if I&#x27;m checking every column to see if they&#x27;re redundant. (Repeat the above process for A, B, and D). I feel like there has to be some kind of dynamic-programming magic (or even Greedy-algorithm) that can figure out the redundant columns all simultaneously. I just don&#x27;t feel like coming up with the algorithm.<p>I can&#x27;t believe that I&#x27;m the first one to investigate this problem. But I don&#x27;t know the &quot;magic word&quot; for this to search on. I don&#x27;t even know how to search the internet for this algorithm, even though I know its important. If anyone knows the most efficient solution, please let me know :-)
shannifinover 4 years ago
One of those tables triggered my Collatz obsession.
dragontamerover 4 years ago
I&#x27;m looking at a lot of these... and it seems like coding of these &quot;tables&quot; needs to be discussed.<p>The most obvious decision-table encoding is the humble bitset. For example:<p><pre><code> A B C | T&#x2F;F --------- 0 0 0 | T 0 0 1 | F 0 1 0 | T 0 1 1 | T 1 0 0 | T 1 0 1 | F 1 1 0 | F 1 1 1 | T </code></pre> Which would be represented as: 0x9D (10011101-binary), a simple 8-bit number (one byte).<p>Unfortunately, this table grows exponentially: 11-variable tables require 2048-bits, but this is still a 256-byte value that can be manipulated in a single-clock tick on AVX2 systems (Zen and Skylake). AVX2 supports AND, OR, NOT, XOR in a single clocktick (heck: Skylake supports 3 256-bit boolean instructions per clocktick. Zen2 supports 4 per clocktick)<p>---------------<p>While 2048-bit (11 variable) tables maximize memory bandwidth through SIMD instructions, the real &quot;rich&quot; instructions exist in the 64-bit world. I don&#x27;t feel like making this post much larger, so I&#x27;ll just note a brief summary now.<p>* Popcnt -- Provides a &quot;Select Count( * ) from table&quot;, so to speak. 1-clock tick on Skylake, 4-per-clock tick on AMD Zen.<p>* PEXT -- Parallel-Extract, a bitwise gather command. 1-clocktick on Skylake (unfortunately slow on Zen: 18 clocks). This is your &quot;Select&quot; statement. Take the 8-bit (3-column) table 0x9D as an example (10011101-binary). PEXT (10011101b, 10101010b) == 1010, which so happens to be equivalent to &quot;Select A,B from table where C=True&quot;.<p>Some other examples:<p><pre><code> PEXT (table, 01010101b) is &quot;Select ... where C=False&quot;. PEXT (table, 11001100b) is &quot;Select ... where B=True&quot;. PEXT (table, 11110000b) is &quot;Select ... where A=True&quot;. PEXT (table, 00001111b) is &quot;Select ... where A=False&quot;. </code></pre> * PDEP -- Parallel Deposit, a bitwise scatter command. This is the exact opposite of PEXT. If you&#x27;re building a larger table from a smaller one, getting ready for a &quot;join&quot; operation, this is useful. Ex: &quot;Expanding&quot; a hypothetical &quot;AB&quot; table into a &quot;ABC&quot; Table (making room for the new &quot;C&quot; column) can use the PDEP instruction.<p>* Summary: PEXT &quot;Reads many column in parallel&quot;, while PDEP &quot;writes many columns in parallel&quot;.<p>* Where PEXT and PDEP are inefficient (ie: everything outside of Intel chips), a careful mix of bitshift-and-AND operations can implement the above operations with reasonable speed.<p>* BLSR -- Reset lowest bit. The 0x9D table is converted into 0x9C, a useful operator for &quot;iterator++&quot; in C++ terms. For example, 0x9D across repeated BLSR commands changes as follows: 10011101, 10011100, 10011000, 10010000, 10000000, and finally 00000000. See TZCNT for more info.<p>* TZCNT -- Trailing Zero Count. A useful operator for &quot;*iterator&quot; in C++ terms. For example, the TZCNT(10011101) == 0, TZCNT(10011100) == 2, TZCNT(10011000) == 3. To convert fully into ABC column form, simply write out the index in binary. For example, &quot;TZCNT(10011000) == 3&quot;, or 011 in binary (A=false, b=true, c=true).<p>&quot;TZCNT(10010000) == 4&quot; becomes 100 in binary (A=true, b=false, c=false).<p>------<p>64-bit Popcnt, BLSR, TZCNT are all implemented in GPUs by the way. PDEP &#x2F; PEXT is an Intel CPU-only trick, but its definitely one of the coolest operators I&#x27;ve seen.<p>Given that all of these instructions can be implemented in 1-clock tick on most hardware (except PDEP &#x2F; PEXT), it seems important to learn how to use these instructions. Especially if you&#x27;re manipulating logic tables in the form described in the blogpost.<p>-------<p>This area of super-optimized bitmask based programming wraps full-round to Relational Algebra and SQL. Its a very intriguing corner of Comp. Sci for me.<p>Arguably, the classic &quot;Table form&quot; that typical SQL-databases use are simply the sparse-matrix form of these bitmasks I described above. Otherwise, the operators are identical in concept.<p>It also should be noted that hardware exists that executes these bitmask-tables extremely efficiently. Its called an FPGA (which these days, are primarily composed of 6-LUTs, or 6-column look up tables, aka 64-bit numbers). Manipulation of these tables alone is Turing complete!