Author Topic: Lists Linking Demo  (Read 15426 times)

0 Members and 1 Guest are viewing this topic.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Lists Linking Demo
« Reply #30 on: January 19, 2019, 04:00:43 pm »
Okay so let's recap.

You started this thread not knowing how to identify a linked list, and changed your mind on what to call this thread after several of us dropped hints.

Instead of conceding anything, you instead doubled down that you were right in the first place.

Then I throw down how it finally works, something a Wikidive could have told you.

You pretend not to read my explanation, and magically divine "aha, I can refer to an example of this that someone else made! (still not a real example)"

I'm not sure where your work enters this discussion.

I was right in the first place. 

It’s just a shame that you can’t comprehend the idea that a name can be associated with different things.  You want to object to this being called a “linked list”, yet you can offer no insight as to what you think it should be called.  It’s a “linking table”, though since it deals primarily with a single column of data, I prefer the term “list” over “table” — and you’ve given no reason why “table/list” shouldn’t be interchangeable.

It’s also a shame that you don’t even know how an $INCLUDE file works, and can’t comprehend that idet$ *is* a linked list — it’s simply an optimized linked list, forced to be kept in order for some odd reason only known by Galleon.   Obviously you have no idea of the very “Linked Lists” you rant so heavily over— you can’t recognize one in its simplest form — nor do you have any comprehension of Linking Tables, yet you like to pretend to be all-knowing on the subject...

Do some research and learn a bit about the subjects you’re arguing over.  As it is, you just come across as a belligerent idiot to me.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Lists Linking Demo
« Reply #31 on: January 19, 2019, 04:11:07 pm »
From Bill's post, now a ways back because I took a nap...

Quote
Proponents of linked lists say "no".

But the problem here is that Ancient Alien Theorists say, "yes."

--------------------------

Yeah the word processing was a good example. I had an idea way back to do something like this...

Say we have 3 records...

1 Pete
2 Steve
3 Rick

Now we missed Bill, and want to insert him into space 1. Yes, with arrays, that would mean we'd have to reassign Pete to 2, Steve to 3 and Rick to 4. However, if we had a tracking string that increased each a record was added, we could just leave these arrays as... a$(1) = "Pete" a$(2) = "Steve" a$(3) = "Rick" and a$(4) = "Bill" So how do we get the order with that tracking string? Like so... tracking$ = "4|1|2|3" it's very easy to manipulate a tracking string with INSTR() and MID$(). So when we need to repront the screen, we consult the tracking string, parse it out and it reprints the order as 4,1,2,3. It also identifies the correct record by the line you are on. If you cursor to line 3...

1 Bill
2 Pete
-->    3 Steve
4 Rick

it looks at the third number in the tracking string, which is 2. This is the array "Steve" was assigned to.

Good stuff guys. Let's see if we can keep it up but without biting each other's heads off. You know we really need Clippy for that.

Pete
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline _vince

  • Seasoned Forum Regular
  • Posts: 422
    • View Profile
Re: Lists Linking Demo
« Reply #32 on: January 20, 2019, 05:07:24 am »
I, too, have a qb64 linked lists example.  These are the proper "doubly linked lists" with the classic textbox/editor example along with storing a whole string per node.

@Steve, the term "linked list" is very specific and well known term in the programming/CS community, like the terms "binary tree", "pointer", "data type", "stack space", "variable", and so on, as it is a specific technique for a particular problem with shuffling items in a list as described by stx.  It is not interchangeable with "linking table" as I've jokingly pointed out, so it's pretty misleading to use the it for something else, that is all.  It would be like calling badminton "basketball" because the ball is basket shaped, logical only if "basketball" wasn't the already established term that it is.

Code: QB64: [Select]
  1. defint a-z
  2.  
  3. type nodeType
  4.         str as _mem
  5.         strLen as integer
  6.  
  7. 'you can add more data to store per node here
  8.  
  9.         n as _mem
  10.         p as _mem
  11.  
  12. type listType
  13.         head as _mem
  14.         tail as _mem
  15.  
  16.  
  17. dim new as listType
  18. dim cur as _mem
  19. dim temp as _mem
  20.  
  21. newList new
  22.  
  23. 'insert garbage
  24. addNodeNext temp, "test1", new.head
  25. addNodeNext temp, "test2", temp
  26. addNodeNext temp, "test3", temp
  27. addNodeNext temp, "test4", temp
  28. addNodePrev temp, "testa", new.tail
  29. addNodePrev temp, "estb", temp
  30. addNodePrev temp, "", temp
  31. addNodePrev temp, "td", temp
  32.  
  33. printList new
  34.  
  35. nextNode cur, new.head
  36.  
  37. cx = 1
  38. cy = 1
  39.  
  40.         cls
  41.         locate 1,1
  42.         printList new
  43.  
  44.         locate cy,cx
  45.         color 0,7
  46.         print chr$(screen(cy,cx))
  47.         color 7,0
  48.  
  49.         do
  50.                 k$ = inkey$
  51.         loop until len(k$)>0
  52.  
  53.         select case k$
  54.         case chr$(13) 'enter
  55.                 if cy = 1 and cx = 1 then
  56.                         addNodePrev cur, "", cur
  57.                 elseif -1 then
  58.                         s$ = readNode$(cur)
  59.                         ss$ = right$(s$, len(s$)-cx+1)
  60.                         s$ = left$(s$, cx-1)
  61.                         writeNode cur, s$
  62.                         addNodeNext cur, ss$, cur
  63.                         cy = cy + 1
  64.                         cx = 1
  65.                 end if
  66.  
  67.         case chr$(8) 'backspace
  68.                 if cx = 1 then
  69.                         if cy > 1 then
  70.                                 s$ = readNode$(cur)
  71.                                 temp = cur
  72.                                 prevNode cur, cur
  73.                                 ss$ = readNode$(cur)
  74.                                 cx = len(ss$) + 1
  75.                                 writeNode cur, ss$ + s$
  76.                                 cy = cy-1
  77.                                 rmNode temp
  78.                         end if
  79.                 elseif cx > 1 then
  80.                         cx = cx - 1
  81.                         s$ = readNode$(cur)
  82.                         ss$ = left$(s$, cx-1) + right$(s$, len(s$)-cx)
  83.                         writeNode cur, ss$
  84.                 end if
  85.  
  86.         case chr$(0)+"K" 'left
  87.                 if cx > 1 then cx = cx - 1
  88.  
  89.         case chr$(0)+"M" 'right
  90.                 s$ = readNode$(cur)
  91.                 if cx <= len(s$) then cx = cx + 1
  92.  
  93.         case chr$(0)+"H" 'up
  94.                 s$ = readNode$(cur)
  95.                 if cy > 1 then
  96.                         prevNode cur, cur
  97.                         s$ = readNode$(cur)
  98.                         if cx > len(s$) then cx = len(s$) + 1
  99.                         cy = cy - 1
  100.                 end if
  101.  
  102.         case chr$(0)+"P" 'down
  103.                 nextNode temp, cur
  104.                 if temp.offset <> new.tail.offset then
  105.                         cur = temp
  106.                         s$ = readNode$(cur)
  107.                         if cx > len(s$) then cx = len(s$) + 1
  108.                         cy = cy + 1
  109.                 end if
  110.  
  111.         case chr$(32) to chr$(128)
  112.                 s$ = readNode$(cur)
  113.                 s$ = mid$(s$,1,cx-1) + k$ + right$(s$, len(s$)-cx+1)
  114.                 writeNode cur, s$
  115.                 cx = cx + 1
  116.         end select
  117.  
  118. loop until k$ = chr$(27)
  119.  
  120. sub addNodeNext (new as _mem, s$, cur as _mem)
  121.         dim node as nodeType
  122.         dim temp as _mem
  123.         dim n as _mem
  124.  
  125.         temp = _memnew(len(node))
  126.  
  127.         nextNode n, cur
  128.  
  129.         node.strLen = len(s$)
  130.         if node.strLen > 0 then
  131.                 node.str = _memnew(len(s$))
  132.                 _memput node.str, node.str.offset, s$
  133.         end if
  134.         node.n = n
  135.         node.p = cur
  136.         _memput temp, temp.offset, node
  137.  
  138.         node = _memget(cur, cur.offset, nodeType)
  139.         node.n = temp
  140.         _memput cur, cur.offset, node
  141.  
  142.         node = _memget(n, n.offset, nodeType)
  143.         node.p = temp
  144.         _memput n, n.offset, node
  145.  
  146.         new = temp
  147.  
  148. sub addNodePrev (new as _mem, s$, cur as _mem)
  149.         dim node as nodeType
  150.         dim temp as _mem
  151.         dim p as _mem
  152.  
  153.         temp = _memnew(len(node))
  154.  
  155.         prevNode p, cur
  156.  
  157.         node.strLen = len(s$)
  158.         if node.strLen > 0 then
  159.                 node.str = _memnew(len(s$))
  160.                 _memput node.str, node.str.offset, s$
  161.         end if
  162.  
  163.         node.n = cur
  164.         node.p = p
  165.         _memput temp, temp.offset, node
  166.  
  167.         node = _memget(cur, cur.offset, nodeType)
  168.         node.p = temp
  169.         _memput cur, cur.offset, node
  170.  
  171.         node = _memget(p, p.offset, nodeType)
  172.         node.n = temp
  173.         _memput p, p.offset, node
  174.  
  175.         new = temp
  176.  
  177. sub rmNode (cur as _mem)
  178.         dim node as nodeType
  179.         dim n as _mem
  180.         dim p as _mem
  181.  
  182.         'remove the string first
  183.         node = _memget(cur, cur.offset, nodeType)
  184.         if node.strLen > 0 then
  185.                 _memfree node.str
  186.         end if
  187.  
  188.         nextNode n, cur
  189.         prevNode p, cur
  190.  
  191.         node = _memget(p, p.offset, nodeType)
  192.         node.n = n
  193.         _memput p, p.offset, node
  194.  
  195.         node = _memget(n, n.offset, nodeType)
  196.         node.p = p
  197.         _memput n, n.offset, node
  198.  
  199.         _memfree cur
  200.  
  201. sub nextNode (new as _mem, old as _mem)
  202.         dim node as nodeType
  203.  
  204.         node = _memget(old, old.offset, nodeType)
  205.         new = node.n
  206.  
  207. sub prevNode (new as _mem, old as _mem)
  208.         dim node as nodeType
  209.  
  210.         node = _memget(old, old.offset, nodeType)
  211.         new = node.p
  212.  
  213. function readNode$ (cur as _mem)
  214.         dim node as nodeType
  215.  
  216.         node = _memget(cur, cur.offset, nodeType)
  217.         if node.strLen = 0 then
  218.                 readNode$ = ""
  219.                 exit function
  220.         end if
  221.         s$ = string$(node.strLen, 0)
  222.         _memget node.str, node.str.offset, s$
  223.  
  224.         readNode$ = s$
  225.  
  226. sub writeNode (cur as _mem, s$)
  227.         dim node as nodeType
  228.  
  229.         'remove old string, freeing memory
  230.         node = _memget(cur, cur.offset, nodeType)
  231.         if node.strLen > 0 then _memfree node.str
  232.  
  233.         'add new string
  234.         node.strLen = len(s$)
  235.         if node.strLen > 0 then
  236.                 node.str = _memnew(len(s$))
  237.                 _memput node.str, node.str.offset, s$
  238.         end if
  239.         _memput cur, cur.offset, node
  240.  
  241. sub newList (new as listType)
  242.         dim node as nodeType
  243.  
  244.         new.head = _memnew(len(node))
  245.         new.tail = _memnew(len(node))
  246.  
  247.         s$ = "head"
  248.         node.strLen = len(s$)
  249.         node.str = _memnew(len(s$))
  250.         node.n = new.tail
  251.         node.p = new.tail
  252.         _memput node.str, node.str.offset, s$
  253.         _memput new.head, new.head.offset, node
  254.  
  255.         s$ = "tail"
  256.         node.strLen = len(s$)
  257.         node.str = _memnew(len(s$))
  258.         node.n = new.head
  259.         node.p = new.head
  260.         _memput node.str, node.str.offset, s$
  261.         _memput new.tail, new.tail.offset, node
  262.  
  263. sub printList (cur as listType)
  264.         dim temp as _mem
  265.  
  266.         nextNode temp, cur.head
  267.         do
  268.                 if temp.offset = cur.tail.offset then exit do
  269.  
  270.                 print readNode$ (temp)
  271.                 nextNode temp, temp
  272.         loop
  273.  
  274. sub rmList (cur as listType)
  275.         dim temp as _mem
  276.         dim temp2 as _mem
  277.  
  278.         nextNode temp, cur.head
  279.         do
  280.                 if temp.offset = cur.tail.offset then exit do
  281.  
  282.                 temp2 = temp
  283.                 nextNode temp, temp2
  284.                 rmNode temp2
  285.         loop
  286.  
  287.         rmNode cur.head
  288.         rmNode cur.tail
  289.  
  290.  

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Lists Linking Demo
« Reply #33 on: January 20, 2019, 11:01:44 am »
Thanks for "pointing" out the C connection, V.

I read about that too, but I know next to nothing about the language, so I didn't want to chime in like I was some kind of authority. What I should have posted is from my limited reading, it appeared to me that the C language uses pointers and coined that phrase, "linked list" in association with putting data in nodes, which I assume are user determined memory locations. I would suspect people in that community would be more sensitive to how that term is defined when using other languages, especially ones like BASIC, which are not associated with using pointers. So the phrase, "What's in a name?" Well, maybe it is all up to who feels they have the most ownership of it. Something similar to this happened to me this morning when I was looking through Bing news and clicked on a headline about PGA Awards. I was expecting a golf article, but instead got some crap about "Producers Guild Awards."

Anyway, QB64 has the C _MEM keywords that bridge the gap into linked lists. I've haven't used those QB64 keywords yet, so thanks V, for posting a node example. It will take me awhile to get to it but hopefully not to long to absorb it. I have always had to rely on the technique I described in the last post or the rather elaborate use of arrays, which I believe that article I read stated was the next best way to link data in a non-pointer language.

Pete
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Lists Linking Demo
« Reply #34 on: January 20, 2019, 11:32:47 am »
Quote
It would be like calling badminton "basketball" because the ball is basket shaped, logical only if "basketball" wasn't the already established term that it is.

Maybe it’s just a regional thing; as I know us hillbillies have our own lingo, but the term I’m using is perfectly suitable around here, just as some places seem to call “soccer” as “football”, even though it’s obviously something different.  I’ve never said a Linked List wasn’t used as you illustrate; I’ve just said it’s also had a different meaning as I’ve known it.  Sometimes “football” is an oval shaped ball you throw in the air, sometimes it’s a circular ball hit with your head — yet, people are generally tolerant enough not to say, “Gee, you’re using that term wrong.”, or at least they’re willing to accept when told, “Well, that’s what we call the game here.”

Like I’ve mentioned, I’m more than willing to call the demo here by another name, if someone can suggest something better to describe what it’s doing.  My biggest problem with folks complaining over the name is simply nobody has actually offered a suggestion of what it should be called. 

I’m thinking of renaming it the  “FIN GUT.BM LIBRARY — Fuck It, Nobody Gets to Use This, But Me LIBRARY.”  If somebody ever wants to do something similar, let them do it themselves and call it whatever the hell they want.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Lists Linking Demo
« Reply #35 on: January 20, 2019, 12:48:14 pm »
I tried your new name but I got a "Duplicate Definition" error when I tried to run it.

Pete :D
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline TempodiBasic

  • Forum Resident
  • Posts: 1792
    • View Profile
Re: Lists Linking Demo
« Reply #36 on: January 20, 2019, 05:39:31 pm »
Pete
What name do you search, in specific?
:-)

following your explanation about linked lists
Quote
Say we have 3 records...

1 Pete
2 Steve
3 Rick

now if we add the forth record "Bill"
in the original list we add at the end the new record...
Original List          Desired List
1 Pete                 1 Bill
2 Steve               2 Pete
3 Rick                 3 Steve
4 Bill                   4 Rick

stored in new variable  tracking$ = "4|1|2|3" --> that is the index of Desired List
now we can order in increasing alphabetical   AlphaIncrease$ = "4|1|3|2"
and we can order in decreasing alphabetical  AlphaDecrese$ = "2|3|1|4"
and we can extract names including "i"  NameI$ = "4|3"
and we can extract names including  two "e"  NameE$="1|2"
the thing becomes interesting....
Programming isn't difficult, only it's  consuming time and coffee

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Lists Linking Demo
« Reply #37 on: January 20, 2019, 09:24:09 pm »
Since you showed some interest in this method, I'll post some working code. It's pretty limited, but it will demonstrate how a tracking string would work...

T$ is the tracking string.
r is the record number in sequential order of entry.

Code: QB64: [Select]
  1. separator$ = "|"
  2. LOCATE , , 1, 7, 7
  3.     _LIMIT 30
  4.     b$ = INKEY$
  5.     IF b$ <> "" THEN
  6.         yy% = CSRLIN
  7.         SELECT CASE b$
  8.             CASE CHR$(0) + "H"
  9.                 IF CSRLIN > 1 THEN LOCATE CSRLIN - 1
  10.             CASE CHR$(0) + "P"
  11.                 IF CSRLIN < 4 THEN LOCATE CSRLIN + 1
  12.             CASE "A" TO "z", "0" TO "9"
  13.                 IF a$(yy%) = "" AND r < 4 THEN
  14.                     PRINT b$;
  15.                     GOSUB text
  16.                 ELSE
  17.                     BEEP ' For this demo, overwrite is not allowed. Only insert.
  18.                 END IF
  19.             CASE CHR$(0) + "R" ' Insert Key
  20.                 IF r < 8 THEN ' Allow up to 8 lines with insert, 4 with just enter.
  21.                     ' Move all entries one line down the screen.
  22.                     PRINT SPACE$(80)
  23.                     GOSUB getseed
  24.                     DO
  25.                         x$ = MID$(t$, seed%, INSTR(seed%, t$, separator$) - seed% + 1) ' Include separator to insure names are unique when concatenated.
  26.                         PRINT SPACE$(80);
  27.                         LOCATE , 1
  28.                         PRINT a$(VAL(x$))
  29.                         seed% = seed% + LEN(x$)
  30.                     LOOP UNTIL seed% > LEN(t$)
  31.                     LOCATE yy%, 1
  32.                     b$ = ""
  33.                     GOSUB text
  34.                 ELSE
  35.                     BEEP ' Only allow 5 entries for this demo
  36.                 END IF
  37.             CASE CHR$(9)
  38.                 CLS
  39.                 seed% = 1
  40.                 FOR i = 1 TO r
  41.                     _DELAY .25
  42.                     x$ = MID$(t$, seed%, INSTR(seed%, t$, separator$) - seed% + 1)
  43.                     PRINT a$(VAL(x$))
  44.                     seed% = seed% + LEN(x$)
  45.                 NEXT
  46.                 yy% = CSRLIN
  47.                 LOCATE 20, 1: PRINT t$
  48.                 LOCATE yy%
  49.             CASE CHR$(27)
  50.                 SYSTEM
  51.         END SELECT
  52.     END IF
  53.  
  54. getseed:
  55. k% = 1: seed% = 1
  56. FOR i% = 1 TO LEN(t$)
  57.     IF k% = yy% THEN EXIT FOR
  58.     IF MID$(t$, i%, 1) = separator$ THEN k% = k% + 1
  59. seed% = i%
  60.  
  61. text:
  62. LOCATE yy%, 1
  63. r = r + 1
  64. a$(r) = b$ + x$
  65. GOSUB getseed
  66. insert$ = separator$
  67. t$ = MID$(insert$ + t$, 1, INSTR(seed%, insert$ + t$, separator$)) + LTRIM$(STR$(r)) + separator$ + MID$(insert$ + t$, INSTR(seed%, insert$ + t$, separator$) + 1)
  68. IF insert$ <> "" THEN t$ = MID$(t$, 2): insert$ = ""
  69. LOCATE 20, 1: PRINT "Tracking String = "; t$
  70. LOCATE yy% + 1, 1 ' Move next line down

Keys are type something and press ENTER. Works with Line Input function.
Insert something with INSERT key then type as usual. Stuff on screen under insert line will move down.
Use the TAB key to clear the screen and it wil print the order correctly, using the tracking string to decide the proper order.

Pete
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline _vince

  • Seasoned Forum Regular
  • Posts: 422
    • View Profile
Re: Lists Linking Demo
« Reply #38 on: January 21, 2019, 09:27:43 am »
Thanks for "pointing" out the C connection, V.

I read about that too, but I know next to nothing about the language, so I didn't want to chime in like I was some kind of authority. What I should have posted is from my limited reading, it appeared to me that the C language uses pointers and coined that phrase, "linked list" in association with putting data in nodes, which I assume are user determined memory locations. I would suspect people in that community would be more sensitive to how that term is defined when using other languages, especially ones like BASIC, which are not associated with using pointers. So the phrase, "What's in a name?" Well, maybe it is all up to who feels they have the most ownership of it. Something similar to this happened to me this morning when I was looking through Bing news and clicked on a headline about PGA Awards. I was expecting a golf article, but instead got some crap about "Producers Guild Awards."

Anyway, QB64 has the C _MEM keywords that bridge the gap into linked lists. I've haven't used those QB64 keywords yet, so thanks V, for posting a node example. It will take me awhile to get to it but hopefully not to long to absorb it. I have always had to rely on the technique I described in the last post or the rather elaborate use of arrays, which I believe that article I read stated was the next best way to link data in a non-pointer language.

Pete

Nothing to do with C specifically, I have not mentioned C, these are general concepts applicable to all languages. _MEM is not a C keyword but a QB64 keyword.

QBasic/QB45 has pointers as well, they exist within the realm controlled by statements such as PEEK, POKE, VARPTR (variable pointer), VARSEG (variable segment), SADD (string address), VARPTR$, DEF SEG, and so on.

I am going to add a few entries related to _MEM to my QB64/FB dictionary.
« Last Edit: January 21, 2019, 09:37:48 am by v »

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: Lists Linking Demo
« Reply #39 on: January 22, 2019, 08:08:07 am »
Looks like we institutionally forgot that I made a full-blown doubly linked list text editor in QB64 a few months ago - the qXed project. I halted the effort to make it a cheap emacs clone because the quality of my internet connection forbids me to test certain online features... however that's irrelevant for this. Like Vince said, a "real" one would use one entire string per node. This one uses one character per node for... certain reasons I can explain. Literally too much to talk about with this program, so I've stripped a few things down to show the basics here.

So HERE is a working example of a doubly-linked list in action. It's a fully-working text editor, but the learning curve is a little steep, and I haven't explained a damn thing so you will think it's full of bugs unless you discover simple stuff.
Code: QB64: [Select]
  1. '$EXEICON:'qXedlogo.ico'
  2. '$RESIZE:ON
  3.  
  4. 'REM $Include: 'sxmath.bi'
  5. 'REM $Include: 'sxript.bi'
  6.  
  7. scrHand& = _NEWIMAGE(90, 30, 0)
  8. SCREEN scrHand&
  9. '_FREEIMAGE scrHand&
  10.  
  11. ' Define fundamental structures.
  12. TYPE Vector
  13.     X AS INTEGER
  14.     Y AS INTEGER
  15.  
  16. TYPE Cell
  17.     Identity AS LONG
  18.     Pointer AS LONG
  19.     Lagger AS LONG
  20.     Content AS STRING * 1
  21.  
  22. DIM SHARED ChainLimit AS LONG
  23. DIM SHARED BOC AS LONG ' Beginning of chain.
  24. DIM SHARED EOC AS LONG ' End of chain.
  25. ChainLimit = 128000
  26. BOC = -1
  27. EOC = ChainLimit
  28.  
  29. ' Define text window properties.
  30. DIM SHARED WindowHeight
  31. DIM SHARED WindowWidth
  32. DIM SHARED VisibleLines
  33. DIM SHARED TopIndent
  34. DIM SHARED LeftIndent
  35. DIM SHARED TextHeight
  36. DIM SHARED TextWidth
  37. DIM SHARED HScroll
  38. DIM SHARED TextWrapping
  39. DIM SHARED TextFormatting
  40. DIM SHARED InsertKey
  41. TopIndent = 1
  42. LeftIndent = 1
  43. WindowWidth = _WIDTH '
  44. WindowHeight = _HEIGHT '- 2
  45. TextHeight = WindowHeight - 2 * TopIndent
  46. TextWidth = WindowWidth - 2 * LeftIndent
  47. HScroll = 1
  48. TextWrapping = 0
  49. TextFormatting = -1
  50. InsertKey = -1
  51.  
  52. ' Initiate text inside window.
  53. DIM SHARED StartIndex
  54. DIM SHARED LineAsMapped(TextHeight) AS STRING
  55. DIM SHARED Cursor1 AS Vector
  56. DIM SHARED Cursor2 AS Vector
  57.  
  58. ' Auxiliary 2D text grid.
  59. DIM SHARED GOLSwitch
  60. DIM SHARED AuxGrid(TextWidth, TextHeight, 2) AS STRING
  61. GOLSwitch = -1
  62.  
  63. ' Load text file into memory if applicable, use example string if not.
  64. DIM SHARED FileName$
  65. IF (c$ <> "") THEN
  66.     q$ = ""
  67.     OPEN c$ FOR INPUT AS #1
  68.     DO WHILE NOT EOF(1)
  69.         LINE INPUT #1, r$
  70.         q$ = q$ + r$ + CHR$(13)
  71.     LOOP
  72.     CLOSE #1
  73.     i = INSTR(c$, ".")
  74.     IF (i <> 0) THEN j = i - 1 ELSE j = LEN(c$)
  75.     FileName$ = LEFT$(c$, j) + "-" + LTRIM$(RTRIM$(STR$(INT(TIMER)))) + ".txt"
  76.     _TITLE FileName$
  77.     FileName$ = "Newfile" + "-" + DATE$ + "-" + LTRIM$(RTRIM$(STR$(INT(TIMER)))) + ".txt"
  78.     q$ = "I sank to the floor. I [experienced] this hallucination of tumbling forward into these fractal geometric spaces made of light and then I found myself in the equivalent of the Pope's private chapel and there were insect elf machines proffering strange little tablets with strange writing on them, and I was aghast, completely appalled, because [in] a matter of seconds . . . my entire expectation of the nature of the world was just being shredded in front of me. I've never actually gotten over it. These self-transforming machine elf creatures were speaking in a colored language which condensed into rotating machines that were like Faberge eggs but crafted out of luminescent superconducting ceramics and liquid crystal gels. All this stuff was just so weird and so alien and so un-English-able that it was a complete shock - I mean, the literal turning inside out of [my] intellectual universe!" + CHR$(13) + CHR$(13) + "This went on for two or three minutes, this situation of [discontinuous] orthogonal dimensions to reality just engulfing me. As I came out of it and the room reassembled itself, I said, " + CHR$(34) + "I can't believe it, it's impossible." + CHR$(34) + " To call that a drug is ridiculous; that just means that you just don't have a word for it and so you putter around and you come upon this sloppy concept [that] something goes into your body and there's a change. It's not like that; it's like being struck by noetic lightning. [Note: " + CHR$(34) + "Noetic" + CHR$(34) + " derives from the theologian Pierre Teilhard de Chardin's " + CHR$(34) + "noosphere" + CHR$(34) + " - the collective consciousness of humankind conceived of as a sort of philosophical virtuality.]" + CHR$(13) + CHR$(13) + "[What] astonished me was [that] . . . in the carpets of Central Asia, in the myths of the Maya, in the visions of an Arcimboldi or a Fra Angelico or a Bosch, there is not a hint, not a clue, not an atom of the presence of this thing, This was more [multiplex] than the universe that we share with each other. It was the victory of Neo-Platonic metaphysics; everything [was] made out of a fourth-dimensional mosaic of energy. I was knocked off my feet, and set myself the goal of understanding this. There was really no choice, you see."
  79.     'q$ = " "
  80.  
  81. _TITLE "qXed"
  82.  
  83. ' Create memory space for string.                                      a
  84. DIM SHARED TheChain(ChainLimit) AS Cell
  85.  
  86. ' Create character list.
  87. CALL Assimilate(q$)
  88.  
  89. ' Prime main loop.
  90. CALL MapText
  91. CALL CalibrateCursor(ID1)
  92. CALL CalibrateCursor(ID2)
  93. CALL PrintEverything
  94.  
  95. DIM SHARED DEBUG$
  96.  
  97. ' Main loop.
  98.     IF (StateChange = 1) THEN
  99.         CALL PrintEverything
  100.     END IF
  101.  
  102.     IF (GOLSwitch = 1) THEN
  103.         CALL ConvertToGrid
  104.         CALL GOL
  105.         CALL ConvertFromGrid
  106.         CALL MapText
  107.         CALL PrintEverything
  108.     END IF
  109.  
  110.     'IF _RESIZE = -1 THEN
  111.     '    _DELAY .1
  112.     '    oldimage& = scrHand&
  113.     '    scrHand& = _NEWIMAGE(_RESIZEWIDTH / 8, _RESIZEHEIGHT / 16, 0)
  114.     '    SCREEN scrHand&
  115.     '    _FREEIMAGE oldimage&
  116.     '    WindowWidth = INT(_RESIZEWIDTH / 8)
  117.     '    WindowHeight = INT(_RESIZEHEIGHT / 16)
  118.     '    TextHeight = WindowHeight - 2 * TopIndent
  119.     '    TextWidth = WindowWidth - 2 * LeftIndent
  120.     '    REDIM LineAsMapped(TextHeight)
  121.     '    REDIM AuxGrid(TextWidth, TextHeight, 2)
  122.     '    CALL MapText
  123.     '    CALL CalibrateCursor(ID1)
  124.     '    CALL CalibrateCursor(ID2)
  125.     '    CALL PrintEverything
  126.     'END IF
  127.  
  128.     _DISPLAY
  129.     _LIMIT 120
  130.  
  131. SUB PrintEverything
  132.     CLS
  133.  
  134.     c$ = "qXed" + " " + DEBUG$
  135.     CALL DisplayText(1, 1, 11, 0, c$)
  136.  
  137.     ' Main text
  138.     FOR i = 1 TO VisibleLines
  139.         c$ = LineAsMapped(i)
  140.         IF ((TextFormatting = 1) AND (TextWrapping <> 2)) THEN
  141.             FOR j = 1 TO TextWidth - LEN(c$)
  142.                 c$ = c$ + "_"
  143.             NEXT
  144.         END IF
  145.         d$ = MID$(c$, HScroll, TextWidth)
  146.         CALL DisplayText(LeftIndent + 1, TopIndent + i, 7, 1, d$)
  147.     NEXT
  148.  
  149.     ' Cursor2
  150.     IF ((Cursor2.X > 0 AND Cursor2.X < WindowWidth) AND ((Cursor2.Y > 0) AND (Cursor2.Y < WindowHeight))) THEN
  151.         p1 = LinearCount(StartIndex, ID1)
  152.         p2 = LinearCount(StartIndex, ID2)
  153.         pe = LinearCount(StartIndex, EOC)
  154.         IF ((p2 > p1) AND (p2 < pe)) THEN
  155.             c$ = TheChain(ID2).Content
  156.             IF (c$ = " ") THEN c$ = "_"
  157.             IF (c$ = CHR$(13)) THEN c$ = "~"
  158.             CALL DisplayText(Cursor2.X, Cursor2.Y, 0, 6, c$)
  159.         END IF
  160.     END IF
  161.  
  162.     ' Cursor1
  163.     IF ((Cursor1.X > 0 AND Cursor1.X < WindowWidth) AND ((Cursor1.Y > 0) AND (Cursor1.Y < WindowHeight))) THEN
  164.         c$ = TheChain(ID1).Content
  165.         IF (c$ = " ") THEN c$ = "_"
  166.         IF (c$ = CHR$(13)) THEN c$ = "~"
  167.         IF ((Cursor1.X = Cursor2.X) AND (Cursor1.Y = Cursor2.Y)) THEN
  168.             a = 16: b = 5
  169.         ELSE
  170.             a = 16: b = 3
  171.         END IF
  172.         CALL DisplayText(Cursor1.X, Cursor1.Y, a, b, c$)
  173.     END IF
  174.  
  175.     ' Cursor status
  176.     d$ = TheChain(ID1).Content
  177.     e$ = TheChain(ID2).Content
  178.     IF ((ASC(d$) = 10) OR (ASC(d$) = 13)) THEN d$ = "~"
  179.     IF ((ASC(e$) = 10) OR (ASC(e$) = 13)) THEN e$ = "~"
  180.     IF (ASC(d$) = 32) THEN d$ = "_"
  181.     IF (ASC(e$) = 32) THEN e$ = "_"
  182.     c$ = "(" + LTRIM$(RTRIM$(STR$(Cursor1.X - LeftIndent))) + "," + LTRIM$(RTRIM$(STR$(Cursor1.Y - TopIndent))) + ":" + " " + LTRIM$(RTRIM$(d$)) + " " + LTRIM$(RTRIM$(STR$(ID1))) + ")"
  183.     a = 0: b = 3
  184.     IF ((Cursor1.X = Cursor2.X) AND (Cursor1.Y = Cursor2.Y)) THEN
  185.         a = 15: b = 5
  186.     END IF
  187.     CALL DisplayText(2, WindowHeight, a, b, c$)
  188.     g$ = "(" + LTRIM$(RTRIM$(STR$(Cursor2.X - LeftIndent))) + "," + LTRIM$(RTRIM$(STR$(Cursor2.Y - TopIndent))) + ":" + " " + LTRIM$(RTRIM$(e$)) + " " + LTRIM$(RTRIM$(STR$(ID2))) + ")"
  189.     a = 6: b = 0
  190.     IF (LinearCount(StartIndex, ID2) > LinearCount(StartIndex, ID1)) THEN
  191.         a = 0: b = 6
  192.     END IF
  193.     CALL DisplayText(3 + LEN(c$), WindowHeight, a, b, g$)
  194.     ' Horizontal scrollbar
  195.     p = LinearCount(NthP(StartIndex, FindID(LeftIndent + 1, Cursor1.Y)), ID1)
  196.     q = LEN(LineAsMapped(Cursor1.Y - TopIndent))
  197.     r = p / q
  198.     IF r > 1 THEN r = 1
  199.     IF r < 0 THEN r = 0
  200.     i = 2 + INT(r * (WindowWidth - 2))
  201.     IF i < 1 THEN i = 1
  202.     IF i > WindowWidth THEN i = WindowWidth
  203.     CALL DisplayText(i, WindowHeight - 1, 8, 7, "^")
  204.  
  205.     ' Vertical scrollbar
  206.     p = LinearCount(ID1, NthP(ID1, ChainLimit + 1))
  207.     q = LinearCount(NthL(ID1, ChainLimit + 1), NthP(ID1, ChainLimit + 1))
  208.     IF (q = 0) THEN r = 1 ELSE r = 1 - p / q
  209.     CALL DisplayText(WindowWidth, 1 + INT(r * (WindowHeight - 1)), 8, 7, "<")
  210.  
  211.     ' Help
  212.     SELECT CASE TextWrapping
  213.         CASE 0: d$ = "Square"
  214.         CASE 1: d$ = "Fluid"
  215.         CASE 2: d$ = "None"
  216.     END SELECT
  217.     c$ = "[F6=Save] [F11=Format] [F12=Wrap: " + d$ + "]"
  218.     IF (TextWrapping = 2) THEN c$ = "[F1/2=HScroll] " + c$
  219.     c$ = c$ + STR$(INT(100 * r)) + "%"
  220.     IF (LEN(c$) >= TextWidth) THEN c$ = LEFT$(c$, TextWidth)
  221.     CALL DisplayText(WindowWidth - LEN(c$), 1, 15, 0, c$)
  222.     c$ = "[Esc=Sync] [Mouse2=Copy] [Mouse3=Paste]"
  223.     IF (InsertKey = 1) THEN c$ = "[Ins] " + c$
  224.     IF (LEN(c$) >= TextWidth) THEN c$ = LEFT$(c$, TextWidth)
  225.     CALL DisplayText(WindowWidth - LEN(c$), WindowHeight, 15, 0, c$)
  226.  
  227.     ' Pointer
  228.     IF ((_MOUSEX >= 1) AND (_MOUSEX <= WindowWidth) AND (_MOUSEY >= 1) AND (_MOUSEY <= WindowHeight)) THEN
  229.         a$ = CHR$(SCREEN(_MOUSEY, _MOUSEX))
  230.         CALL DisplayText(_MOUSEX, _MOUSEY, 0, 15, a$)
  231.     END IF
  232.  
  233.     COLOR 15, 0
  234.  
  235.  
  236. SUB Assimilate (a AS STRING)
  237.     ' Load a string to initialize chain.
  238.     FOR k = 1 TO ChainLimit
  239.         TheChain(k).Identity = 0
  240.     NEXT
  241.     StartIndex = 1
  242.     PreviousIdentity = BOC
  243.     NextIdentity = NextOpenIdentity(StartIndex)
  244.     FOR k = 1 TO LEN(a)
  245.         j = NextIdentity
  246.         TheChain(j).Identity = j
  247.         TheChain(j).Content = ReFormat$(MID$(a, k, 1))
  248.         TheChain(j).Lagger = PreviousIdentity
  249.         PreviousIdentity = j
  250.         IF (k < LEN(a)) THEN
  251.             NextIdentity = NextOpenIdentity(j)
  252.             TheChain(j).Pointer = NextIdentity
  253.         ELSE
  254.             TheChain(j).Pointer = EOC
  255.         END IF
  256.         PRINT TheChain(j).Content
  257.     NEXT
  258.     ID1 = StartIndex
  259.     ID2 = NthP(ID1, ChainLimit + 1)
  260.  
  261. FUNCTION ReFormat$ (a AS STRING)
  262.     c$ = a
  263.     IF c$ = CHR$(10) THEN c$ = CHR$(13)
  264.     IF c$ = CHR$(9) THEN c$ = "    "
  265.     ReFormat = c$
  266.  
  267. FUNCTION NthP (a AS LONG, b AS LONG)
  268.     ' Returns the address that is b jumps ahead of address a.
  269.     i = a
  270.     IF (i <> EOC) THEN
  271.         k = 0
  272.         j = 0
  273.         DO WHILE (k < b)
  274.             k = k + 1
  275.             j = TheChain(i).Identity
  276.             i = TheChain(j).Pointer
  277.             IF (i = EOC) THEN EXIT DO
  278.         LOOP
  279.     END IF
  280.     NthP = j
  281.  
  282. FUNCTION NthPC (a AS LONG, b AS STRING)
  283.     ' Returns the address holding b first enLinearCountered from a.
  284.     i = a
  285.     DO
  286.         j = TheChain(i).Identity
  287.         i = TheChain(j).Pointer
  288.         IF (TheChain(j).Content = b) THEN EXIT DO
  289.         IF (i = EOC) THEN
  290.             j = BOC
  291.             EXIT DO
  292.         END IF
  293.     LOOP
  294.     NthPC = j
  295.  
  296. FUNCTION NthL (a AS LONG, b AS LONG)
  297.     ' Returns the address that is b jumps behind address a.
  298.     i = a
  299.     k = 0
  300.     DO WHILE k < b
  301.         k = k + 1
  302.         j = TheChain(i).Identity
  303.         i = TheChain(j).Lagger
  304.         IF (i = BOC) THEN EXIT DO
  305.     LOOP
  306.     NthL = j
  307.  
  308. FUNCTION NextOpenIdentity (a AS LONG)
  309.     ' Returns first nonzero identity.
  310.     FOR j = a TO ChainLimit
  311.         IF (TheChain(j).Identity = 0) THEN EXIT FOR
  312.     NEXT
  313.     IF (j > ChainLimit) THEN
  314.         PRINT "Out of memory: "; ChainLimit
  315.         SLEEP
  316.         SYSTEM
  317.     END IF
  318.     NextOpenIdentity = j
  319.  
  320. FUNCTION BackBreak (a AS LONG)
  321.     ' Function for scrolling up.
  322.     j = a
  323.     lastbreak = 0
  324.     c$ = ""
  325.     DO
  326.         IF (j = BOC) THEN EXIT DO
  327.         k = TheChain(j).Lagger
  328.         IF (k = BOC) THEN
  329.             lastbreak = j
  330.             EXIT DO
  331.         END IF
  332.         j = k
  333.         d$ = TheChain(j).Content
  334.         IF ((TextWrapping = 1) AND (d$ = " ")) THEN lastbreak = j
  335.         c$ = d$ + c$
  336.         IF (TextWrapping <> 2) AND (LEN(c$) = TextWidth) THEN EXIT DO
  337.         IF (d$ = CHR$(13)) THEN EXIT DO
  338.     LOOP
  339.     IF (lastbreak <> 0) THEN j = TheChain(lastbreak).Identity
  340.     BackBreak = j
  341.  
  342. SUB InsertBefore (a AS LONG, b AS STRING)
  343.     ' Inserts a single cell before address a in the chain.
  344.     j = NextOpenIdentity(a)
  345.     al = TheChain(a).Lagger
  346.     TheChain(j).Identity = j
  347.     TheChain(j).Pointer = a
  348.     TheChain(j).Lagger = al
  349.     TheChain(j).Content = ReFormat$(b)
  350.     TheChain(a).Lagger = j
  351.     IF (al = BOC) THEN StartIndex = j ELSE TheChain(al).Pointer = j
  352.  
  353. SUB InsertAfter (a AS LONG, b AS STRING)
  354.     ' Inserts a single cell after address a in the chain.
  355.     j = NextOpenIdentity(a)
  356.     ap = TheChain(a).Pointer
  357.     TheChain(j).Identity = j
  358.     TheChain(j).Pointer = ap
  359.     TheChain(j).Lagger = a
  360.     TheChain(j).Content = ReFormat$(b)
  361.     TheChain(a).Pointer = j
  362.     IF (ap <> EOC) THEN TheChain(ap).Lagger = j
  363.  
  364. SUB InsertRange (a AS LONG, b AS STRING)
  365.     ' Inserts a sub-chain anywhere.
  366.     FOR k = 1 TO LEN(b)
  367.         c$ = MID$(b, k, 1)
  368.         CALL InsertBefore(a, c$)
  369.     NEXT
  370.  
  371. SUB UnlinkCell (a AS LONG)
  372.     ' Remove single cell from chain and clear identity.
  373.     ap = TheChain(a).Pointer
  374.     al = TheChain(a).Lagger
  375.     IF ((ap = EOC) AND (al = BOC)) THEN
  376.         TheChain(a).Content = " "
  377.         'ID1 = a
  378.         'ID2 = ID1
  379.     ELSE
  380.         TheChain(a).Identity = 0
  381.         IF ((ap <> EOC) AND (al <> BOC)) THEN
  382.             TheChain(al).Pointer = ap
  383.             TheChain(ap).Lagger = al
  384.         END IF
  385.         IF (ap = EOC) THEN TheChain(al).Pointer = EOC
  386.         IF (al = BOC) THEN
  387.             StartIndex = ap
  388.             TheChain(ap).Lagger = BOC
  389.         END IF
  390.     END IF
  391.  
  392. SUB UnlinkRange (a AS LONG, b AS LONG)
  393.     ' Remove sub-chain and clear identity of each cell.
  394.     bp = TheChain(b).Pointer
  395.     al = TheChain(a).Lagger
  396.     IF ((al = BOC) AND (bp = EOC)) THEN
  397.         CALL UnlinkRange(NthP(a, 2), b)
  398.         TheChain(a).Content = " "
  399.         TheChain(a).Pointer = bp
  400.     ELSE
  401.         k = a
  402.         DO WHILE ((k <> b) AND (k <> EOC))
  403.             TheChain(k).Identity = 0
  404.             k = TheChain(k).Pointer
  405.         LOOP
  406.         TheChain(b).Identity = 0
  407.         TheChain(bp).Lagger = al
  408.         IF (al = BOC) THEN StartIndex = bp ELSE TheChain(al).Pointer = bp
  409.     END IF
  410.  
  411. FUNCTION LinearCount (a AS LONG, b AS LONG)
  412.     ' Returns number of links between two addresses.
  413.     i = a
  414.     k = 0
  415.     DO WHILE (i <> b)
  416.         k = k + 1
  417.         j = TheChain(i).Identity
  418.         i = TheChain(j).Pointer
  419.         IF (i = EOC) THEN EXIT DO
  420.     LOOP
  421.     LinearCount = k
  422.  
  423. FUNCTION LinearCount2 (a AS LONG, b AS LONG, c AS LONG)
  424.     ' Returns number of links between two addresses, with exit condition.
  425.     i = a
  426.     k = 0
  427.     DO WHILE (i <> b)
  428.         k = k + 1
  429.         j = TheChain(i).Identity
  430.         i = TheChain(j).Pointer
  431.         IF (i = EOC) THEN EXIT DO
  432.         IF (k = c) THEN EXIT DO
  433.     LOOP
  434.     LinearCount2 = k
  435.  
  436. FUNCTION Projection$ (a AS LONG, b AS LONG)
  437.     ' Returns the linear content for all address between a and b, inclusive.
  438.     DIM TheReturn AS STRING
  439.     TheReturn = ""
  440.     IF (a = b) THEN
  441.         TheReturn = TheChain(a).Content
  442.     ELSE
  443.         j = a
  444.         DO
  445.             c$ = TheChain(j).Content
  446.             TheReturn = TheReturn + c$
  447.             k = TheChain(j).Pointer
  448.             IF (j = b) THEN EXIT DO
  449.             IF (k = EOC) THEN EXIT DO
  450.             j = k
  451.         LOOP
  452.     END IF
  453.     Projection$ = TheReturn
  454.  
  455. SUB MapText
  456.     IF (TextFormatting = 1) THEN br$ = "~" ELSE br$ = " "
  457.     j = StartIndex
  458.     i = 1
  459.     q$ = ""
  460.     d$ = ""
  461.     DO ' Begin with any left-over text from previous iteration.
  462.         q$ = d$
  463.         d$ = ""
  464.         r = TextWidth - LEN(q$)
  465.         IF (TextWrapping <> 2) THEN k1 = NthP(j, r) ELSE k1 = EOC
  466.         k2 = NthPC(j, CHR$(13))
  467.         IF (TextWrapping <> 2) THEN c1 = LinearCount(j, k1) ELSE c1 = LinearCount2(j, k1, TextWidth * TextHeight)
  468.         c2 = LinearCount(j, k2)
  469.         IF (c2 = 0) THEN ' Line is blank-returned.
  470.             k = k2
  471.             q$ = q$ + br$
  472.             j = NthP(k, 2)
  473.         ELSE
  474.             IF (c1 = c2) THEN ' Possible end of chain.
  475.                 k = TheChain(k1).Lagger
  476.                 q$ = q$ + Projection$(j, k)
  477.                 j = NthP(k, 2)
  478.             END IF
  479.             IF (c1 < c2) THEN ' Width limit case (not always maximum).
  480.                 k = k1
  481.                 q$ = q$ + Projection$(j, k)
  482.                 j = NthP(k, 2)
  483.             END IF
  484.             IF (c1 > c2) THEN ' Break return somewhere in line (not first).
  485.                 k = k2
  486.                 q$ = q$ + Projection$(j, TheChain(k).Lagger) + br$
  487.                 n = TheChain(k).Pointer
  488.                 IF (n <> EOC) THEN j = n
  489.             END IF
  490.         END IF
  491.         IF (TextWrapping = 1) THEN ' Wrap text at first space from right, send remainder to next line.
  492.             IF (LEN(q$) >= TextWidth) THEN
  493.                 FOR m = LEN(q$) TO 1 STEP -1
  494.                     c$ = MID$(q$, m, 1)
  495.                     IF (c$ = " ") OR (c$ = "-") THEN
  496.                         q$ = LEFT$(q$, m)
  497.                         EXIT FOR
  498.                     END IF
  499.                     d$ = c$ + d$
  500.                     IF (m = 1) THEN ' Line is too long for allowed space and contains no wrapping characters.
  501.                         q$ = LEFT$(q$, TextWidth)
  502.                         d$ = ""
  503.                         EXIT FOR
  504.                     END IF
  505.                 NEXT
  506.             END IF
  507.         END IF
  508.         LineAsMapped(i) = q$
  509.         i = i + 1
  510.         IF (i >= TextHeight) THEN EXIT DO
  511.         IF (j = k) THEN EXIT DO
  512.     LOOP
  513.     VisibleLines = i - 1
  514.  
  515. FUNCTION StateChange
  516.     DIM TheReturn
  517.     MH = 0
  518.     MW = 0
  519.     MT = 0
  520.         MH1 = _MOUSEBUTTON(1)
  521.         MH2 = _MOUSEBUTTON(2)
  522.         MH3 = _MOUSEBUTTON(3)
  523.         MW = _MOUSEWHEEL
  524.         IF (MW <> 0) THEN MT = MW
  525.     LOOP
  526.     MW = MT
  527.  
  528.     IF (MH1 = -1) THEN
  529.         ' Move Cursor1 among text.
  530.         MH = 1
  531.         IF ((_MOUSEX > LeftIndent) AND (_MOUSEX < TextWidth + LeftIndent + 1) AND (_MOUSEY > TopIndent) AND (_MOUSEY < TopIndent + TextHeight)) THEN
  532.             Cursor1.X = _MOUSEX
  533.             q = LeftIndent + LEN(LineAsMapped(_MOUSEY - TopIndent))
  534.             IF (Cursor1.X > q) THEN Cursor1.X = q
  535.             Cursor1.Y = _MOUSEY
  536.             CALL ReassignID1
  537.         END IF
  538.         ' Move by vertical scrollbar.
  539.         IF (_MOUSEX = WindowWidth) THEN
  540.             i = NthL(ID1, ChainLimit + 1)
  541.             j = NthP(ID1, ChainLimit + 1)
  542.             IF (_MOUSEY = WindowHeight) THEN i = j
  543.             IF (_MOUSEY > 1) AND (_MOUSEY < WindowHeight) THEN
  544.                 t = LinearCount(i, j)
  545.                 f = _MOUSEY / WindowHeight
  546.                 FOR k = 1 TO t
  547.                     IF (k / t >= f) THEN EXIT FOR
  548.                     i = TheChain(i).Pointer
  549.                 NEXT
  550.             END IF
  551.             StartIndex = i
  552.             ID1 = i
  553.         END IF
  554.         ' Move by horizontal scrollbar.
  555.         IF (_MOUSEY = WindowHeight - 1) THEN
  556.             j = ID1
  557.             i = NthP(StartIndex, FindID(LeftIndent + 1, Cursor1.Y))
  558.             'IF (_MOUSEX = windowwidth - 1) THEN i = NthP(StartIndex, FindID(LeftIndent + LEN(LineAsMapped(Cursor1.Y - TopIndent)), Cursor1.Y) + (HScroll - 1))
  559.             IF (_MOUSEX > 1) AND (_MOUSEX < WindowWidth) THEN
  560.                 t = LEN(LineAsMapped(Cursor1.Y - TopIndent))
  561.                 f = _MOUSEX / WindowWidth
  562.                 FOR k = 1 TO t
  563.                     IF (k / t >= f) THEN EXIT FOR
  564.                     i = TheChain(i).Pointer
  565.                 NEXT
  566.             END IF
  567.             ID1 = i
  568.             d = LinearCount(StartIndex, i) - LinearCount(StartIndex, j)
  569.             IF (TextWrapping = 2) THEN HScroll = HScroll + d
  570.             IF HScroll < 1 THEN HScroll = 1
  571.         END IF
  572.     END IF
  573.     IF (MH2 = -1) THEN
  574.         ' Move Cursor2 and copy anything between Cursor1 and Cursor2 to clipboard.
  575.         MH = 1
  576.         IF (_MOUSEX > LeftIndent) AND (_MOUSEX < TextWidth + LeftIndent + 1) AND (_MOUSEY > TopIndent) AND (_MOUSEY < TopIndent + TextHeight + 1) THEN
  577.             Cursor2.X = _MOUSEX
  578.             q = LeftIndent + LEN(LineAsMapped(_MOUSEY - TopIndent))
  579.             IF (Cursor2.X > q) THEN Cursor2.X = q
  580.             Cursor2.Y = _MOUSEY
  581.             CALL ReassignID2
  582.             IF (LinearCount(StartIndex, ID2) > LinearCount(StartIndex, ID1)) THEN _CLIPBOARD$ = Projection$(ID1, ID2)
  583.         END IF
  584.     END IF
  585.     IF (MH3 = -1) THEN
  586.         ' Paste at Cursor1 position.
  587.         MH = 1
  588.         'IF (LinearCount(StartIndex, ID2) >= LinearCount(StartIndex, ID1)) THEN
  589.         CALL InsertRange(ID1, _CLIPBOARD$)
  590.     END IF
  591.     IF (MW = -1) THEN
  592.         ' Wheel up
  593.         MH = 1
  594.         StartIndex = BackBreak(StartIndex)
  595.         CALL ReassignID1
  596.     END IF
  597.     IF (MW = 1) THEN
  598.         ' Wheel down
  599.         MH = 1
  600.         IF (VisibleLines > 1) THEN
  601.             StartIndex = NthP(StartIndex, LEN(LineAsMapped(1)) + 1)
  602.             CALL MapText
  603.         END IF
  604.         CALL ReassignID1
  605.     END IF
  606.  
  607.     KH = 0
  608.     '''k$ = ""
  609.     '''k$ = INKEY$
  610.     '''IF k$ <> "" THEN KH = ASC(k$)
  611.     KH = _KEYHIT
  612.  
  613.     ' Bksp
  614.     IF (KH = 8) THEN
  615.         r = TheChain(ID1).Pointer
  616.         q = TheChain(ID1).Lagger
  617.         CALL UnlinkCell(ID1)
  618.         IF ((r = EOC) AND (q = BOC)) THEN
  619.         ELSE
  620.             IF (q <> BOC) THEN ID1 = q ELSE ID1 = r
  621.             IF (r = EOC) THEN ID2 = ID1
  622.         END IF
  623.     END IF
  624.     ' Tab
  625.     IF (KH = 9) THEN CALL InsertRange(ID1, "    ")
  626.     ' Esc
  627.     IF (KH = 27) THEN
  628.         IF (ID2 <> ID1) THEN ID2 = ID1 ELSE ID2 = StartIndex 'NthL(ID2, 2)
  629.     END IF
  630.     ' Enter, Alphanumerics
  631.     IF (KH = 13) OR ((KH >= 32) AND (KH <= 126)) THEN
  632.         IF (InsertKey = -1) THEN
  633.             IF (ID1 = ID2) THEN
  634.                 CALL InsertBefore(ID1, LTRIM$(RTRIM$(CHR$(KH))))
  635.             ELSE
  636.                 CALL InsertAfter(ID1, LTRIM$(RTRIM$(CHR$(KH))))
  637.                 ID1 = NthP(ID1, 2)
  638.             END IF
  639.         ELSE
  640.             TheChain(ID1).Content = LTRIM$(RTRIM$(CHR$(KH)))
  641.             IF (ID1 = ID2) THEN
  642.                 ID1 = NthP(ID1, 2)
  643.                 ID2 = ID1
  644.             ELSE
  645.                 ID1 = NthP(ID1, 2)
  646.             END IF
  647.         END IF
  648.         IF ((TextWrapping = 2) AND (Cursor1.X - LeftIndent = TextWidth)) THEN HScroll = HScroll + 1
  649.     END IF
  650.     ' F1
  651.     IF (KH = 15104) THEN
  652.         IF (TextWrapping = 2) THEN
  653.             HScroll = HScroll - 1
  654.             IF (HScroll < 1) THEN HScroll = 1
  655.             CALL ReassignID1
  656.             CALL ReassignID2
  657.         END IF
  658.     END IF
  659.     ' F2
  660.     IF (KH = 15360) THEN
  661.         IF (TextWrapping = 2) THEN
  662.             HScroll = HScroll + 1
  663.             CALL ReassignID1
  664.             CALL ReassignID2
  665.         END IF
  666.     END IF
  667.     ' F5
  668.     IF (KH = 16128) THEN
  669.         q$ = Projection$(NthL(ID1, ChainLimit + 1), NthP(ID1, ChainLimit + 1))
  670.         Assimilate q$
  671.     END IF
  672.     ' F4
  673.     'IF (KH = 15872) THEN
  674.     'CALL InsertRange(NthP(ID2, 2), CHR$(10) + "=" + CoreProcess$(Projection(ID1, ID2)))
  675.     'CALL InsertRange(NthP(ID2, 2), CHR$(10) + "=" + SxriptEval$(Projection(ID1, ID2)))
  676.     'ID2 = StartIndex
  677.     'END IF
  678.     ' F6
  679.     IF (KH = 16384) THEN
  680.         OPEN FileName$ FOR OUTPUT AS #1
  681.         q$ = Projection$(NthL(ID1, ChainLimit + 1), NthP(ID1, ChainLimit + 1))
  682.         PRINT #1, q$
  683.         CLOSE #1
  684.     END IF
  685.     ' F7
  686.     IF (KH = 16640) THEN
  687.         GOLSwitch = -GOLSwitch
  688.     END IF
  689.     ' F8 IF (KH = 16896) THEN
  690.     ' Home
  691.     IF (KH = 18176) THEN
  692.         IF (TextWrapping = 2) THEN HScroll = 1
  693.         Cursor1.X = LeftIndent + 1
  694.         CALL ReassignID1
  695.     END IF
  696.     ' UpArrow
  697.     IF (KH = 18432) THEN
  698.         IF (Cursor1.Y > TopIndent + 1) THEN
  699.             Cursor1.Y = Cursor1.Y - 1
  700.         ELSE
  701.             StartIndex = BackBreak(StartIndex)
  702.         END IF
  703.         q = LEN(LineAsMapped(Cursor1.Y - TopIndent)) + 1
  704.         IF (Cursor1.X > q) THEN Cursor1.X = q
  705.         CALL ReassignID1
  706.     END IF
  707.     ' PgUp
  708.     IF (KH = 18688) THEN
  709.         FOR k = 1 TO INT(TextHeight / 2)
  710.             StartIndex = BackBreak(StartIndex)
  711.         NEXT
  712.         CALL ReassignID1
  713.     END IF
  714.     ' LeftArrow
  715.     IF (KH = 19200) THEN
  716.         ID1 = NthL(ID1, 2)
  717.         IF (TextWrapping = 2) THEN
  718.             IF (Cursor1.X = LeftIndent + 1) THEN
  719.                 IF (HScroll > 1) THEN
  720.                     HScroll = HScroll - 1
  721.                 ELSE
  722.                     j = Cursor1.Y - TopIndent - 1
  723.                     IF (j >= 1) THEN
  724.                         k = LEN(LineAsMapped(j)) - TextWidth + 1
  725.                         IF (k >= 1) THEN
  726.                             HScroll = k
  727.                         END IF
  728.                     END IF
  729.                 END IF
  730.             END IF
  731.         ELSE
  732.             IF ((Cursor1.X - LeftIndent = 1) AND Cursor1.Y - TopIndent = 1) THEN
  733.                 StartIndex = BackBreak(StartIndex)
  734.             END IF
  735.         END IF
  736.     END IF
  737.     ' RightArrow
  738.     IF (KH = 19712) THEN
  739.         ID1 = NthP(ID1, 2)
  740.         m = Cursor1.X - LeftIndent
  741.         n = LEN(LineAsMapped(Cursor1.Y - TopIndent)) - HScroll + 1
  742.         IF (TextWrapping = 2) THEN
  743.             IF (m >= TextWidth) THEN
  744.                 HScroll = HScroll + 1
  745.                 CALL ReassignID1
  746.             END IF
  747.             IF (m >= n) THEN
  748.                 j = Cursor1.Y - TopIndent + 1
  749.                 IF ((j <= TextHeight) AND (VisibleLines > 1)) THEN HScroll = 1
  750.             END IF
  751.         ELSE
  752.             IF ((m >= n) AND (Cursor1.Y - TopIndent = VisibleLines)) THEN
  753.                 IF (VisibleLines > 1) THEN StartIndex = NthP(StartIndex, LEN(LineAsMapped(1)) + 1)
  754.             END IF
  755.         END IF
  756.     END IF
  757.     ' End
  758.     IF (KH = 20224) THEN
  759.         Cursor1.X = LeftIndent + LEN(LineAsMapped(Cursor1.Y - TopIndent))
  760.         CALL ReassignID1
  761.         IF (TextWrapping = 2) THEN
  762.             q = LEN(LineAsMapped(Cursor1.Y - TopIndent)) - TextWidth + 1
  763.             IF (q >= 1) THEN HScroll = q
  764.         END IF
  765.     END IF
  766.     ' DownArrow
  767.     IF (KH = 20480) THEN
  768.         IF (Cursor1.Y = TopIndent + VisibleLines) THEN
  769.             IF (VisibleLines > 1) THEN
  770.                 StartIndex = NthP(StartIndex, LEN(LineAsMapped(1)) + 1)
  771.                 CALL MapText
  772.             END IF
  773.         ELSE
  774.             Cursor1.Y = Cursor1.Y + 1
  775.         END IF
  776.         q = LEN(LineAsMapped(Cursor1.Y - TopIndent)) + 1
  777.         IF (Cursor1.X > q) THEN
  778.             Cursor1.X = q
  779.         END IF
  780.         CALL ReassignID1
  781.     END IF
  782.     ' PgDn
  783.     IF (KH = 20736) THEN
  784.         FOR k = 1 TO INT(TextHeight / 2)
  785.             IF (VisibleLines > 1) THEN
  786.                 StartIndex = NthP(StartIndex, LEN(LineAsMapped(1)) + 1)
  787.                 CALL MapText
  788.             END IF
  789.         NEXT
  790.         CALL ReassignID1
  791.     END IF
  792.     ' Insert
  793.     IF (KH = 20992) THEN
  794.         InsertKey = -InsertKey
  795.     END IF
  796.     ' Del
  797.     IF (KH = 21248) THEN
  798.         IF (LinearCount(StartIndex, ID2) > LinearCount(StartIndex, ID1)) THEN
  799.             r = TheChain(ID2).Pointer
  800.             q = TheChain(ID1).Lagger
  801.             p = ID1
  802.             CALL UnlinkRange(ID1, ID2)
  803.             IF ((r = EOC) AND (q = BOC)) THEN
  804.                 ID1 = p
  805.                 ID2 = ID1
  806.                 StartIndex = p
  807.             ELSE
  808.                 IF (q <> BOC) THEN ID1 = q ELSE ID1 = r
  809.                 ID2 = NthP(ID1, 2)
  810.             END IF
  811.         END IF
  812.     END IF
  813.     ' F11
  814.     IF (KH = 34048) THEN TextFormatting = -TextFormatting
  815.     ' F12
  816.     IF (KH = 34304) THEN
  817.         TextWrapping = TextWrapping + 1
  818.         IF (TextWrapping > 2) THEN TextWrapping = 0
  819.         ID1 = StartIndex
  820.         ID2 = ID1
  821.         HScroll = 1
  822.     END IF
  823.     ' Exit sequence
  824.     TheReturn = 0
  825.     IF ((MH <> 0) OR (KH > 0)) THEN
  826.         TheReturn = 1
  827.         CALL MapText
  828.         CALL CalibrateCursor(ID1)
  829.         CALL CalibrateCursor(ID2)
  830.         ' Cursor sync and autoscrolling.
  831.         IF (Cursor1.Y > TopIndent + TextHeight - 1) THEN StartIndex = NthP(StartIndex, LEN(LineAsMapped(1)) + 1)
  832.     END IF
  833.     _KEYCLEAR
  834.     StateChange = TheReturn
  835.  
  836. SUB CalibrateCursor (a AS LONG)
  837.     ' Place Cursor under ID on rendered line.
  838.     s = StartIndex
  839.     IF ((TextWrapping = 2) AND (HScroll > 1)) THEN s = NthP(s, HScroll)
  840.     c = LinearCount(s, a)
  841.     k = 0
  842.     i = -1
  843.     FOR j = 1 TO VisibleLines
  844.         n = LEN(LineAsMapped(j))
  845.         IF (k + n < c) THEN
  846.             k = k + n
  847.         ELSE
  848.             i = c - k + 1
  849.             EXIT FOR
  850.         END IF
  851.     NEXT
  852.     IF (i >= LeftIndent + LEN(LineAsMapped(j))) THEN
  853.         IF (j <= VisibleLines) THEN
  854.             i = 1
  855.             j = j + 1
  856.         END IF
  857.     END IF
  858.     IF (a = ID1) THEN
  859.         Cursor1.X = LeftIndent + i
  860.         Cursor1.Y = TopIndent + j
  861.     END IF
  862.     IF (a = ID2) THEN
  863.         Cursor2.X = LeftIndent + i
  864.         Cursor2.Y = TopIndent + j
  865.     END IF
  866.  
  867. FUNCTION FindID (a AS INTEGER, b AS LONG)
  868.     ' Find identity under a map location.
  869.     RelX = a - LeftIndent
  870.     RelY = b - TopIndent
  871.     FOR k = 1 TO RelY - 1
  872.         t = t + LEN(LineAsMapped(k))
  873.     NEXT
  874.     t = t + RelX
  875.     FindID = t
  876.  
  877. SUB ReassignID1
  878.     ' Reassign identity under Cursor1.
  879.     ID1 = NthP(StartIndex, FindID(Cursor1.X, Cursor1.Y) + (HScroll - 1))
  880.  
  881. SUB ReassignID2
  882.     ' Reassign identity under Cursor2.
  883.     ID2 = NthP(StartIndex, FindID(Cursor2.X, Cursor2.Y) + (HScroll - 1))
  884.  
  885. SUB ConvertToGrid
  886.     FOR j = 1 TO VisibleLines
  887.         c$ = LineAsMapped(j)
  888.         FOR i = 1 TO LEN(c$) - 1 ' BR offset to exclude break return at line end.
  889.             AuxGrid(i, j, 1) = MID$(c$, i, 1)
  890.         NEXT
  891.     NEXT
  892.  
  893. SUB ConvertFromGrid
  894.     q$ = ""
  895.     FOR j = 1 TO VisibleLines
  896.         FOR i = 1 TO LEN(LineAsMapped(j)) - 1
  897.             q$ = q$ + AuxGrid(i, j, 1)
  898.         NEXT
  899.         q$ = q$ + CHR$(13) ' Undoes BR offset.
  900.     NEXT
  901.     Assimilate q$
  902.  
  903. SUB GOL
  904.     FOR j = 1 TO VisibleLines
  905.         FOR i = 1 TO LEN(LineAsMapped(j)) - 1
  906.             c$ = AuxGrid(i, j, 1)
  907.             IF (c$ = " ") THEN c$ = "0" ELSE c$ = "1"
  908.             AuxGrid(i, j, 1) = c$
  909.             AuxGrid(i, j, 2) = c$
  910.         NEXT
  911.     NEXT
  912.     FOR j = 2 TO VisibleLines - 2 ' BR offset.
  913.         FOR i = 2 TO LEN(LineAsMapped(j)) - 2 ' BR offset.
  914.             c$ = AuxGrid(i, j, 1)
  915.             a1 = VAL(AuxGrid(i - 1, j + 1, 1))
  916.             a2 = VAL(AuxGrid(i, j + 1, 1))
  917.             a3 = VAL(AuxGrid(i + 1, j + 1, 1))
  918.             a4 = VAL(AuxGrid(i - 1, j, 1))
  919.             a6 = VAL(AuxGrid(i + 1, j, 1))
  920.             a7 = VAL(AuxGrid(i - 1, j - 1, 1))
  921.             a8 = VAL(AuxGrid(i, j - 1, 1))
  922.             a9 = VAL(AuxGrid(i + 1, j - 1, 1))
  923.             t = a1 + a2 + a3 + a4 + a6 + a7 + a8 + a9
  924.             IF (c$ = "1") THEN
  925.                 SELECT CASE t
  926.                     CASE IS < 2
  927.                         AuxGrid(i, j, 2) = "0"
  928.                     CASE 2
  929.                         AuxGrid(i, j, 2) = "1"
  930.                     CASE 3
  931.                         AuxGrid(i, j, 2) = "1"
  932.                     CASE IS > 3
  933.                         AuxGrid(i, j, 2) = "0"
  934.                 END SELECT
  935.             ELSE
  936.                 IF (t = 3) THEN AuxGrid(i, j, 2) = "1"
  937.             END IF
  938.         NEXT
  939.     NEXT
  940.     FOR j = 1 TO VisibleLines
  941.         FOR i = 1 TO LEN(LineAsMapped(j)) - 1
  942.             c$ = AuxGrid(i, j, 2)
  943.             IF (c$ = "0") THEN c$ = " " ELSE c$ = CHR$(219)
  944.             AuxGrid(i, j, 1) = c$
  945.             AuxGrid(i, j, 2) = c$
  946.         NEXT
  947.     NEXT
  948.  
  949. SUB ThrowOutput
  950.     'i = NthP(ID1, ChainLimit + 1)
  951.     'FOR k = 1 TO LogTextCount
  952.     '    CALL InsertRange(i, LogText(k) + CHR$(13))
  953.     'NEXT
  954.     LogTextCount = 0
  955.     CALL MapText
  956.     ID1 = NthP(ID1, ChainLimit + 1)
  957.     CALL CalibrateCursor(ID1)
  958.     CALL CalibrateCursor(ID2)
  959.     DO WHILE (Cursor1.Y > TopIndent + TextHeight - 1)
  960.         StartIndex = NthP(StartIndex, LEN(LineAsMapped(1)) + 1)
  961.         CALL MapText
  962.         CALL CalibrateCursor(ID1)
  963.         CALL CalibrateCursor(ID2)
  964.     LOOP
  965.     ID1 = NthP(ID1, ChainLimit + 1)
  966.     CALL PrintEverything
  967.  
  968. SUB DisplayText (Col AS INTEGER, Row AS INTEGER, Shade1 AS INTEGER, Shade2 AS INTEGER, Text AS STRING)
  969.     COLOR Shade1, Shade2
  970.     _PRINTSTRING (Col, Row), Text
  971.     'LOCATE Row, Col: PRINT Text
  972.  
  973. 'REM $Include: 'sxmath.bm'
  974. 'REM $Include: 'sxript.bm'
« Last Edit: January 22, 2019, 08:51:11 am by STxAxTIC »
You're not done when it works, you're done when it's right.

Offline _vince

  • Seasoned Forum Regular
  • Posts: 422
    • View Profile
Re: Lists Linking Demo
« Reply #40 on: January 22, 2019, 09:37:10 am »
Interesting idea, STxExTIC.  I've tried yours before, feels like a novel text editor concept, the next vim, stevesque one could even say.  It would be interesting to see a clone of vi (way simpler than vim) in QB64 and your routines have all the functionality for it. Maybe Steve  's got something up his sleeve for the field of text editing, let us know by this eve.

FYI, my own LL routines could be repurposed to hold a single character per node like so:
Code: QB64: [Select]
  1. type nodeType
  2.   'remove this:
  3.         'str as _mem
  4.         'strLen as integer
  5.   'for:
  6.         c as string * 1
  7.  
  8.         n as _mem
  9.         p as _mem
  10.  
then you would no longer need readNode$ and writeNode and so on. Handling a string per node takes up a large portion of the code as they're a separate data structure on their own to be allocated, reallocated, and deallocated.

Next thing I will investigate is LLs in freebasic which has the C-style pointer syntax which is very expressive for these kinds of data structures.  Most of the LL functions are one liners.  LLs can also be repurposed to act as stacks, queues, trees, and other list-like structures.  Beyond LLs we have the countless variants of trees, networks, meshes and so on.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Lists Linking Demo
« Reply #41 on: January 22, 2019, 10:50:51 am »
I recognize "a linked list" as a special data structure that has some advantages over arrays specially as STxAxTIC and v have described and demonstrated with editors.

But getting back to what Steve was trying to show was a different kind of linking of lists. He didn't seem to take to ideas of databasing and the advanced database term "relational database", so I have another simpler term to try: Concatenate

Isn't Steve simply concatenating lists, adding one to another and showing manipulations of lists both individually and as a whole concatenated list. The tricky part I imagine, is dealing with mixing of different type lists.
def concatenate.PNG
* def concatenate.PNG (Filesize: 7.66 KB, Dimensions: 482x129, Views: 185)

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Lists Linking Demo
« Reply #42 on: January 22, 2019, 12:55:06 pm »
Isn't Steve simply concatenating lists, adding one to another and showing manipulations of lists both individually and as a whole concatenated list. The tricky part I imagine, is dealing with mixing of different type lists.

It isn’t really concatenation, as the lists “link” element by element.  “Links things together in a chain or series...”

So a list of “1, 2, 3, 4” concatetaned with “5, 6, 7, 8” produces “1,2,3,4,5,6,7,8”...  Think “dog” + “food” = “dog-food”.

This takes those 2 lists and “links” them so “1 - 5”, “2 - 6”, “3 - 7”, “4 - 8” all share the same properties and work and behave alike. 

What we’re doing here is Linking Relational Database Tables — https://www.relationaldbdesign.com/relational-database-design/module2/linking-relational-database-tables.php.

Quote
The power of relational databases stems from the fact that tables can be linked, enabling users to gain access to data across tables.

Two tables are linked when they have a matching field, that is, a field in each table containing similar data.

And that’s what the demo here does for us.  It just doesn’t seem to me that “Linking Relational Database Table Management Library” comes across as a BASIC sounding name, so “Linked Table Library” (or “Linked List Library” as I like to call it since a “LIST” *is* just a table with a single column of information) seems much simpler and more appropriate to me.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!