NYCJUG/2006-03-08

From J Wiki
Jump to navigation Jump to search

Meeting Overview

We had a good, meandering meeting that somehow managed to touch on all the agenda items. I'll present them in the order of the agenda though that's not necessarily the order in which we touched on them.

For the "Beginner's regatta", I presented an update of a topic from last month's meeting: different ways of waiting for an external command to finish. As is often the case in these matters, there are routines in the standard J libraries superior to my hand-crafted solutions.

In this case, the functions in "task.ijs", fork and shell, do a fine job of invoking a command and either returning immediately or waiting for it to finish. Using the "wait" command I've been invoking to test for any lag in waiting for a command to finish, one could enter

   load 'task'
   _1 fork 'cmd /c wait 5'

where the "_1" left argument tells fork to wait for the command to finish. The small disadvantage of invoking the command processor "cmd" (necessary because "wait" is an internal DOS command), is that it brings up a DOS window. I haven't figured out a way around this. For example tests, see "cmdWaitTestingFork.txt".

Regular Expression Classification Table

We explored a technique I've been using with good success: table-driven file classification using regular expressions. This is a table consisting most essentially of a column of regular expressions and another column of executable expressions. Files sent to a central server during the day are processed by a routine that classifies files according to which regular expression they match, then running the corresponding expression on the files thus selected. See "RegExTableTests.txt" for an example.

A most important restriction is that each file be classified by only one of the regular expressions; also, each expression should pick up a likely file. This quickly highlights the necessity of building the test suite alongside the code, a common practice in "Extreme Programming". However, as the test suite grows in complexity, it, too, needs to be tested. This raises the question of how we test the tests. For instance, we need to be sure we'll pick up a duplicate classification but we don't want to pollute our good table with deliberate errors. Dan had some good suggestions on dynamically modifying the table to accomplish this.


Learning/Teaching Array Thinking

We discussed a problem from the "TopCoder" website involving finding the longest prime-free intervals for a given range of positive integers. See "topcoderfarfromprimeEG.txt" for a fuller explanation.

We compared the looping solutions presented with more array-oriented approaches but were stymied to come up with any sort of general scalar-to-array method. Much of array-oriented thinking comes from a different initial approach that might just require lots of practice.

  1. Beginner's regatta: update to "wait for external command": standard functions in "task.ijs": fork and shell; see also "cmdWaitTestingFork.txt".
  2. Show-and-tell: can Tom tell us about openGL? Look at "findEEparametric.txt" and the points generated by the brute force solution in the "EqnEquality" files, e.g. "findEqnEqualityPrecis.txt".
  3. Advanced topics: dropOffs.ijs - using regular expressions (see "testingTheRegExpTable.txt", "regExChars.txt") to classify files and testing the classifications ->"addNewRegExp&Test.txt", "RegExpTable.xls" and "RegExTableTests.txt"
  4. Learning and teaching J: quotesWin example or "topcoderfarfromprime.ijx"? See "topcoderfarfromprimeEG.txt".

   The three cavities of the body are
   the head cavity, the tooth cavity,
   and the abominable cavity.
     - The Revenge of Anguished English by Richard Lederer

Beginner's regatta

Testing "Fork" with the "Wait" Command

I tried using J's fork command with the system wait command to test if the fork returns immediately or waits for the underlying command to finish.

   6!:2 '_1 fork ''cmd /c wait 5'''   NB. Sometimes initial invocation is slow.
9.6700656
   6!:2 '_1 fork ''cmd /c wait 5'''
5.4788475
   6!:2 '_1 fork ''cmd /c wait 8'''
8.0502921

NB. Works fine except that it pops up DOS window: how to get rid of it?
   _1 fork 'wait 5'
   _1 fork 'cmd /c wait 5'
   _1 fork 'cmd /c wait 5 | NUL'
NB. These didn't work.  What's the difference between a desktop shortcut that runs
NB. with a normal window versus a minimized one?
   wmin=. fread '\amisc\Wait3WinMin.lnk'
   wnorm=. fread '\amisc\Wait3WinNorm.lnk'
   wmin-:wnorm
0
NB. The difference is this one byte in position 60.
   I. wmin~:wnorm
60
   a. i. 60{&>wmin;<wnorm
7 1
NB. Values for minimized versus normal.  But can we invoke a .LNK from command
NB. line?
   _1 fork 'cmd /c C:\amisc\Wait3WinMin.lnk'
   _1 fork 'C:\amisc\Wait3WinMin.lnk'
   _1 fork 'cmd /c C:\amisc\Wait3WinNorm.lnk'
NB. These don't work.

NB. Run test suite used on other command waiters:
   load '\amisc\j\nycjug\200603\commandWait.ijs'
   6!:2 'tmtsk=. testWaitGapTask 10'
55.895464
   tmtsk
 1 1.9605981
 2 1.9954598
 3 2.9473634
 4 4.0659215
 5 5.0212745
 6 5.9370571
 7 7.0856086
 8 7.9757402
 9 8.9272701
10 9.9783364
   tmtsk=. tmtsk,testWaitGapTask 10 [ tmtsk=. tmtsk,testWaitGapTask 10 [ ...
   $tmtsk
4 10 2
   tmtsk=. tmtsk,testWaitGapTask 10 [ tmtsk=. tmtsk,testWaitGapTask 10 [ ...
   dd=. 'C:\amisc\J\NYCJUG\200603\'
   (dd) fileVar 'tmtsk'              NB. Save these timings and get the others.
+-+---------+
|1|TMTSK.DAT|
+-+---------+
   (<'C:\amisc\J\NYCJUG\200602\') unfileVar_WS_&.> 'tmwts';'tmwe'
+-----------------------------------------------+------------------------...
|+-+-------------------------------------------+|+-+---------------------...
||1|+-------------------------+---------+-----+|||1|+--------------------...
|| ||C:\amisc\J\NYCJUG\200602\|TMWTS.DAT|tmwts|||| ||C:\amisc\J\NYCJUG\20...
|| |+-------------------------+---------+-----+||| |+--------------------...
|+-+-------------------------------------------+|+-+---------------------...
+-----------------------------------------------+------------------------...
   $tmwts
10 10 2
   $tmtsk
11 10 2

   >,&.>}."1&.>mean &.>tmwe;tmwts;tmtsk
1.59798 2.25568 3.22976 3.77794 5.78405 6.77765 6.83266 7.66218 8.58564 9.55089
1.02312 2.02381 3.01202 3.97675 4.99607 6.01084 7.05268 8.00437 8.96521 9.99008
1.06253 2.00606 2.99934  4.0352 4.98803 6.00605 6.99741 8.01578 8.97279 10.0006
   >,&.>}."1&.>stddev &.>tmwe;tmwts;tmtsk
0.584813  0.678296   1.17039   1.31878  0.634437  0.467846   2.14851    2.6231   2.11798   2.86598
0.401127 0.0330933 0.0299177 0.0394623 0.0152714 0.0276781 0.0526376 0.0477404 0.0262925  0.032303
0.314534 0.0604375 0.0418279 0.0269598 0.0278296 0.0496729 0.0616933 0.0539134 0.0450852 0.0332828
   plot >,&.>}."1&.>mean &.>tmwe;tmwts;tmtsk
   plot >,&.>}."1&.>stddev &.>tmwe;tmwts;tmtsk
   NB. the latter 2 are much more congruent...
   6!:2 'tmtsk60=. testWaitGapTask 60'
1770.5921

Code to Test Fork Overhead

[This is ancient code which no longer has a chance of working (except for testWaitGapTask, at the end) because it requires tools from another age - like oleautomation and win32api - and for other reasons as well.]

NB.* commandWait.ijs: test how well we can wait for an external command.

load '~user/code/cmdtool.ijs'
coinsert 'fldir' [ load 'filefns'

NB.          +.-------- Either use "cmdtool" (requires WinSh) ------------+.

testWaitGap=: 3 : 0
   aa=: '' conew 'cmdtool'
   tmwt=. (y.,2)$0
   for_ii. >:i.y. do.
      cmd=. 'wait ',":ii
      tm=. 6!:2 'runCmd__aa ''',cmd,''''
      tmwt=. (ii,tm) (<:ii)}tmwt
   end.
   tmwt
)

NB.          +.-------- or use "winexec" (requires Windows API) ------------+.

winexec=: 'WinExec' win32api       NB. Run external command

NB.* runCmdWait: run external command and wait for it to finish.
runCmdWait=: 3 : 0
   pid0=. cmdShellNums ''           NB. Get process ids of all command shells running.
   winexec y.;1                     NB. This starts another command shell.
   pidn=. pid0-.~cmdShellNums ''    NB. New processes since 1st check may include the
   while. +./(cmdShellNums '')e. pidn do. wait 1 end.  NB. .bat file we just started.
)

NB.* cmdShellNums: get process numbers of all DOS command shells running.
cmdShellNums=: 3 : 0
   if. 0=#y. do. y.=. 'CMD.EXE' end.
   ;n2j&.>(+./&>(boxopen y.) E.&.>toupper&.>1{pss)#0{pss=. getPS ''
)

getPS=: 3 : 0
NB.* getPS: get information of running processes: col 0 is process ids,
NB. col 1 is class and text name.
   alph=. '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
   rnd=. (?6##alph){alph
   tmpfl=. (endSlash getTempDir ''),'PS',rnd,'.txt'
   delFile tmpfl
   sink=. winexec ('cmd /C ps > ',tmpfl);0
   wait 0.1              NB. Wait 0.1 second for command to finish
   max=. 9               NB.  and try 9 times just for sake of bogged system.
   ctr=. 1
   while. ctr<:max do.
       if. fexist tmpfl do. break.
       else. wait (ctr%10) [ ctr=. >:ctr end.     NB. Wait longer each time since
   end.                                           NB.  must be slow.
   ps=. dlsp&.>f2v tmpfl
   ps=. (2+(3{.&.>ps)i.<'PID')}.ps                NB. Drop header
   delFile tmpfl
   ps=. (wh{.&.>ps),:(>:&.>wh=. ps i.&.>' ')}.&.>ps
)

testWaitGapWE=: 3 : 0
   tmwt=. (y.,2)$0
   for_ii. >:i.y. do.
      cmd=. 'cmd /c wait ',":ii
      tm=. 6!:2 'runCmdWait ''',cmd,''''
      tmwt=. (ii,tm) (<:ii)}tmwt
   end.
   tmwt
)

difTest=: 0 : 0
   fread batfl=. (ad=. '\amisc\'),'testWait.bat'
del foo.tmp
wait %1
echo > foo.tmp

   fexist ad,'foo.tmp' [ runCmdWait 'cmd /c ',batfl,' 1'
   fexist ad,'foo.tmp' [ runCmdWait 'cmd /c ',batfl,' 2'
   fexist ad,'foo.tmp' [ runCmdWait 'cmd /c ',batfl,' 3'
   fexist ad,'foo.tmp' [ runCmdWait 'cmd /c ',batfl,' 4'
   fexist ad,'foo.tmp' [ runCmdWait 'cmd /c ',batfl,' 5'
   fexist ad,'foo.tmp' [ runCmdWait 'cmd /c ',batfl,' 6'
   fexist ad,'foo.tmp' [ runCmdWait 'cmd /c ',batfl,' 7'
   fexist ad,'foo.tmp' [ runCmdWait 'cmd /c ',batfl,' 8'
)

The following should work assuming a Windows "cmd" shell and a "wait" command that takes a number of seconds as an argument.

NB.          +.-------- or use "task.ijs" ------------+.
testWaitGapTask=: 3 : 0
   tmwt=. (y.,2)$0
   for_ii. >:i.y. do.
      cmd=. 'cmd /c wait ',":ii
      tm=. 6!:2 '_1 fork cmd'
      tmwt=. (ii,tm) (<:ii)}tmwt
   end.
   tmwt
)

Show-and-tell

We looked at a problem posed by Mike Daly on the J Forum: he wanted to know what J syntax to use to find solutions to a^2 + b^2 + c^2 = (a - b)(b - c)(c - a). He is aware that there are infinitely many solutions.

There were a few replies to this, after which Mike clarified that he was looking for solutions over integers. See "findEqnEqualityPrecis.txt" for a solution. However, I had a method for real numbers that I liked but became interested in the shape of the solution-space. To see this, I needed a way to plot 3-dimensional points.

Fortunately, Oleg Kobchenko provided a way to do this with his "plot3d.ijs" contribution to the forum. The plots are somewhat interesting and there are a number of useful lessons to be learned from different aspects of solving the system of equations, but I intend to write these up later. See "ptsWtw*.jpg" for example plots.

Initial Attempts

Here is how we set out to find solutions to this equation. Please note that this code was written in an earlier version of J that used "y." for "y" and "x." for "x".

   eqn0=: 3 : '+/*:y.'           NB. Left-hand side of equation
   eqn0 1 2 3
14
   eqn1=: 3 : '*/2-/\y.,{.y.'    NB. Right-hand side of equation
   eqn1 1 2 3
2
   rndn=. ?1000$1e3              NB. 1000 random integers
   $3 eqn0\rndn
998
   $3 eqn1\rndn
998
   I.(3 eqn0\rndn)=3 eqn1\rndn  NB. Any equalities?

   rndn=. _1000+?100000$2001    NB. Try again
   I.(3 eqn0\rndn)=3 eqn1\rndn

   rndn=. _10000+?100000$20001  NB. and again
   I.(3 eqn0\rndn)=3 eqn1\rndn

   rndn=. (_10000+?100000$20001)%>:?100000$1000   NB. and again
   I.(3 eqn0\rndn)=3 eqn1\rndn

   diffeqns=: 3 : '(eqn0 y.)-eqn1 y.'   NB. Consolidate the tests
   diffs=. 3 diffeqns\rndn
   (<./,>./)diffs
_2.0998913e10 2.9516792e10
   (<./,>./)|diffs                      NB. We care about absolute differences
0.01071377 2.9516792e10

Clearly this is very unlikely to give us a solution. However, if we ignore the integer constraint, we should be able to iterate toward a solution.

Iterating Toward a Solution

Let's start with some of the closest solutions from our random set and iterate around those values.

   eqn0=: 3 : '+/*:y.'                 NB. Set up equations
   eqn1=: 3 : '*/2-/\y.,{.y.'
   diffeqns=: 3 : '(eqn0 y.)-eqn1 y.'  NB. and differencer.

We start by picking wide range of random arguments and looking at the smallest and largest values.

   (<./,>./)rndn=. (_1e4+?1e5$20001)%>:?1e5$1e3
_9879 9658

Check how close these points are: the best (closest to zero) and the worst.

   (<./,>./)|diffs=. 3 diffeqns\rndn
0.010086928 5.5282426e10

Let's start with the ten best points.

   close1s=. 10{./:|diffs     NB. Pick 10 closest results as
   seeds=. close1s{3[\rndn    NB.  starting points...
_0.63838384 0.37931034  _17.451852
 _6.4783599  5.4468339   6.1741868
. . .
_0.10789474 0.10091743   0.3027933

   diffeqns"1 seeds
0.010086928 0.011770021 _0.023991408 ... 0.096197183

Start with 1st seed and look a little bit around it in 3-space.

   $&.>nn=. (0{seeds) +&.>/ <steps _1 1 11
+--+--+--+
|11|11|11|
+--+--+--+

We are using steps to choose evenly-spaced points.

steps=: 3 : 0
NB.* steps: vector of numbers from num, to num, in numsteps steps.
   'from to numsteps'=. y.
   from+(to-from)*(numsteps-1)%~i.numsteps
)

We make an 11x11x11 neighborhood around a seed. We use an odd number of points to include original point so we can do no worse.

   $>>,&.>/&.>/ nn
11 11 11 3

   <./,|dd=. diffeqns&> >,&.>/&.>/ nn
0.006545809

This is an improvement. Checking the index of the point on which it is based:

   >ix=. <($dd)#:(,|dd)i.<./,|dd
5 5 0

This new point and how close it is:

   >ix{>,&.>/&.>/ nn
_0.63838384 0.37931034 _17.461852
   diffeqns >ix{>,&.>/&.>/ nn NB. Check it...
0.0065475394

Now we can use this point as new centroid for tighter neighborhood constrained to plus or minus 0.1 from it.

   nn=. (>ix{>,&.>/&.>/ nn) +&.>/ <steps _0.1 0.1 11
   <./,|dd=. diffeqns&> >,&.>/&.>/ nn
0.00054704513

This shows further improvement. Which point is that?

   >ix=. <($dd)#:(,|dd)i.<./,|dd
5 5 4
   >ix{>,&.>/&.>/ nn          NB. New point...
_0.63838384 0.37931034 _17.481852

We can keep on doing this until we see no significant improvement, like this where we start with another seed:

   nn=. (1{seeds) +&.>/ <steps _1 1 11
   <./,|dd=. diffeqns&> >,&.>/&.>/ nn
0.011770021
   diffeqns 1{seeds
0.011770021

This shows no improvement, so we tighten the search neighborhood and try again.

   nn=. (1{seeds) +&.>/ <steps _0.1 0.1 13
   <./,|dd=. diffeqns&> >,&.>/&.>/ nn
0.0029753954
   >ix=. <($dd)#:(,|dd)i.<./,|dd
8 4 4
   >ix{>,&.>/&.>/ nn
_6.4450266 5.4135006 6.1408534

Make it tighter around this better point and continue to find better values.

   nn=. (>ix{>,&.>/&.>/ nn) +&.>/ <steps _0.001 0.001 13
   <./,|dd=. diffeqns&> >,&.>/&.>/ nn
9.0611054e_5
   >ix=. <($dd)#:(,|dd)i.<./,|dd
3 12 12
   >ix{>,&.>/&.>/ nn
_6.4455266 5.4145006 6.1418534

Picturing a Solution

Thanks to some 3-D graphing help from Oleg Kobchenko, I was able to get an idea of what the solution space to this equality looks like.

PtsBtw10&100Dis.jpg

Advanced topics

We look at using regular expressions to classify files and at how we test the classifications.

Using Regular Expressions

Testing the Regular Expression Table

   0!:101 wdclipread ''
          createNewDatedDir ymd                 NB. New day, new sub-directory
          'TSNOW FLSNOW'=: listFiles DODIR      NB. Current files' info
          noChange=. ONCETHRU*.FLSNOW-:FLSOLD   NB. Change or 1st invocation
   noChange
0

   0!:101 wdclipread ''
      rc=. 0
      checkDeletions '' [ ASSERR=: 'Error in "checkDeletions"'
0
      ASSERR=: 'Error in "checkAdditions"'

   0!:101 wdclipread ''
   0<checkAdditions ''
1

   0!:101 wdclipread ''
          renameUnderscoresToBlanks DODIR  NB. E.G. "inner_space.txt"->"inner space.txt"
1684 1616
          wait 2                           NB. Let first set of renames finish.
2
          wait 2 [ renameDatedFls DODIR    NB. E.G. "dif 1-3-06.xls"->"dif 01-03-06
2

   0!:101 wdclipread ''
          'TSNOW FLSNOW'=: listFiles DODIR NB. Update current file info for name ch
   FLSNOW
+-----------------------------------+-------------------+------+---+------+
|27280 03-10-2006 CASH.xls          |2006 3 10 9 28 29  |37888 |rw-|-----a|
+-----------------------------------+-------------------+------+---+------+
|xxx80 02-15-2006 CASH.xls          |2006 2 15 9 46 47  |51200 |rw-|------|
+-----------------------------------+-------------------+------+---+------+
|AAFUtils.ijs                       |2006 2 27 18 30 21 |35972 |rw-|------|
+-----------------------------------+-------------------+------+---+------+
|Copy of Rpt Somefund VP NAV (2).xls|2006 2 14 19 49 44 |24576 |rw-|------|
+-----------------------------------+-------------------+------+---+------+
|Copy_of_Rpt_Somefund_VP_NAV_(2).xls|2006 2 23 18 20 46 |25088 |rw-|------|
+-----------------------------------+-------------------+------+---+------+
|Sometg Manager 03-06-06.xls        |2006 3 7 11 34 29  |104960|rw-|------|
+-----------------------------------+-------------------+------+---+------+
|dropOffs.ijs                       |2006 2 23 16 3 21  |17320 |rw-|------|
+-----------------------------------+-------------------+------+---+------+
|fileFns.ijs                        |2006 2 24 10 18 48 |20547 |rw-|------|
+-----------------------------------+-------------------+------+---+------+
|fof.ijs                            |2006 2 27 17 54 34 |67019 |rw-|------|
+-----------------------------------+-------------------+------+---+------+
|stopDropOffs.ijs                   |2005 12 21 13 37 41|1263  |rw-|------|
+-----------------------------------+-------------------+------+---+------+

   0!:101 wdclipread ''
      fls=. jfi dir DODIR,'*.*' [ ASSERR=: 'Error in "checkFiles"'  NB. Get file names
      reconfls=. (1{"1 FT) rxmatch&.>/toupper&.>{."1 fls            NB.  and classify
      reconfls=. _1~:({.@:,)&>reconfls                              NB.  them.

   0!:101 wdclipread ''
      wfr=. +./reconfls    NB. Which files recognized
   wfr
1 0 0 0 0 0 0 0 0 0
   0{reconfls
0 0 0 0 0 0 0 0 0 0
   reconfls
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
   $reconfls
16 10
   $FT
16 3
   $FLSNOW
10 5
  I. {."1 reconfls
5
   5{FT
+--------------+--------------------------------------------------------------------+-----------------------+
|AAF daily cash|^(27280|16250|38660|27350) [0-9]{1,2}-[0-9]{1,2}-[0-9]{2,4} CASH.XLS|moveCashFilesToDatedDir|
+--------------+--------------------------------------------------------------------+-----------------------+

   0!:101 wdclipread ''
   pl=. 's'#~1~:nrf=. +/wfr

   0!:101 wdclipread ''
          logMsg_logger_ 'Recognized ',(":nrf),' file',pl,':'
New file at 2006/03/10 10:09:28.391:
"27280 3-10-2006 CASH.xls"
Recognized 1 file:
          msg=. punclist wfr#_1|.&.>({."1 fls),&.><'""'
          logMsg_logger_ ' ',msg,'.'
New file at 2006/03/10 10:09:28.391:
"27280 3-10-2006 CASH.xls"
Recognized 1 file:
 "27280 03-10-2006 CASH.xls".

   0!:101 wdclipread ''
   I.+./|:reconfls
5
   fltype=. 5

   0!:101 wdclipread ''
          recogfls=. (fltype{reconfls)#{."1 fls NB.  get the file names and run

   0!:101 wdclipread ''
   ' recogfls',~,>(<fltype,FTHDR i. <'PGM'){FT
moveCashFilesToDatedDir recogfls
   recogfls
+-------------------------+
|27280 03-10-2006 CASH.xls|
+-------------------------+
   moveCashFilesToDatedDir recogfls
+-+
|1|
+-+
   qts''
2006 3 10 10 13 48.446

regExChars.txt

There are two different sets of metacharacters: those that are recognized anywhere in the pattern except within square brackets, and those that are recognized in square brackets. Outside square brackets, the metacharacters are as follows:

  \      general escape character with several uses
  ^      assert start of string (or line, in multiline mode)
  $      assert end of string (or line, in multiline mode)
  .      match any character except newline (by default)
  [      start character class definition
  |      start of alternative branch
  (      start subpattern
  )      end subpattern
  ?      extends the meaning of (
         also 0 or 1 quantifier
         also quantifier minimizer
  *      0 or more quantifier
  +      1 or more quantifier
         also "possessive quantifier"
  {      start min/max quantifier

Part of a pattern that is in square brackets is called a "character class". In a character class the only metacharacters are:

  \      general escape character
  ^      negate the class, but only if the first character
  -      indicates character range
  [      POSIX character class (only if followed by POSIX syntax)
  ]      terminates the character class

The following sections describe the use of each of the metacharacters....

Introduction

Professor Massimo Di Pierro (DePaul University) in his article "What is Blockchain?", Computing in Engineering and Science Vol. 19 No. 5 2017 https://www.computer.org/csdl/magazine/cs/2017/05/mcs2017050092/13rRUyv53Jl (Reprinted in Computing Edge April 2018)[1] provides a quick review of Blockchain technology and a small implementation example in Python. The example uses JSON library to serialize Python data into a string that can then be run through a hash to complete the block chain. It also takes advantage of Python's built in list management features. There is nothing Python specific in the implementation and making some reasonable design choices a J language implementation is straight forward.

  1. M. Pierro, "What Is the Blockchain?" in Computing in Science & Engineering, vol. 19, no. 05, pp. 92-95, 2017. doi: 10.1109/MCSE.2017.3421554 keywords: {bitcoin;contracts;peer-to-peer computing;digital signatures;authentication} url: https://doi.ieeecomputersociety.org/10.1109/MCSE.2017.3421554