AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

[Concept Showcase] [Library] AHK Linked Lists

 
Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
olegbl



Joined: 13 Dec 2006
Posts: 48

PostPosted: Fri Nov 07, 2008 7:57 pm    Post subject: [Concept Showcase] [Library] AHK Linked Lists Reply with quote

So, I've been toying around with the idea of converting AHKA to a linked list. I doubt I'll actually do that, but I did create a working Linked List. (I'd like to say right here that AHK REAAAALLLLLYYYYY needs referenced variables AKA proper dereferencing).
The functionality is very basic so far.
Available Linked List Functions:
new
copy
add - full forwards/backwards index support (SA style, not AHKA style)
get - full forwards/backwards index support (SA style, not AHKA style)
print - displays something like "Austria@12345678 -> Brazil@12346789 -> 0"

The linked list involves two libraries:
The linked list node (LLN)
The linked list (LL)

Put both of them into your /Lib/ folder, and then you can use it...
(Well, you should know that if you're looking at a concept showcase)

LLN
Code:
; class LLN // Linked List Node
; {
;   UInt size; // Size of data
;   UInt next; // Pointer to next node
;   Char [] data; // Array of characters (up to 64 KB worth of characters)
; }
LLN_Allocate()
{
   global
   LLN_ += 1
   LLN_%LLN_% := 0
   VarSetCapacity(LLN_%LLN_%, 64 * 1024 + 8, 0) ; Change the 64 here to the number of KB you want each list node to be capable of storing
   return &LLN_%LLN_%
}
LLN_New(data, next=0)
{
   global LLN_
   node := LLN_Allocate()
   
   size := StrLen(data)
   NumPut(size, node + 0, 0, "UInt")
   NumPut(next, node + 0, 4, "UInt")
   Loop, %size%
      NumPut(Asc(SubStr(data, A_Index, 1)), node + 0, 7 + A_Index, "UChar")
   
   ;next := LLN_GetNext(node)
   ;MsgBox, Created [%node%] -> [%next%]
   
   return node
}
LLN_SetContent(node, data)
{
   size := StrLen(data)
   NumPut(size, node + 0, 0, "UInt")
   Loop, %size%
      NumPut(Asc(SubStr(data, A_Index, 1)), node + 0, 7 + A_Index, "UChar")
}
LLN_SetNext(node, next)
{
   NumPut(next, node + 0, 4, "UInt")
}
LLN_GetContent(node)
{
   size := NumGet(node + 0, 0, "UInt")
   data := ""
   Loop, %size%
      data .= Chr(NumGet(node + 0, 7 + A_Index, "UChar"))
   return data
}
LLN_GetNext(node)
{
   return NumGet(node + 0, 4, "UInt")
}
LLN_GetSize(node)
{
   return NumGet(node + 0, 0, "UInt")
}
LLN_Print(node)
{
   if (node != 0)
      return LLN_GetContent(node) . "@" . node . " -> " . LLN_Print(LLN_GetNext(node))
   else
      return node
}


LL
Code:
; class LL // Linked List
; {
;   UInt head; // Pointer to top node
;   UInt size; // Number of nodes in the array
; }
LL_Allocate()
{
   global
   LL_ += 1
   LL_%LL_% := 0
   VarSetCapacity(LL_%LL_%, 8, 0)
   return &LL_%LL_%
}
LL_New()
{
   global LL_
   arr := LL_Allocate()
   NumPut(0, arr + 0, 0, "UInt")
   NumPut(0, arr + 0, 4, "UInt")
   return arr
}
LL_Copy(arr1)
{
   arr2 := LL_Allocate()
   size := LL_GetSize(arr1)
   NumPut(0, arr2 + 0, 0, "UInt")
   NumPut(size, arr2 + 0, 4, "UInt")
   Loop, %size%
   {
      LL_Add(arr2, LL_Get(arr1, A_Index))
   }
   return arr2
}
LL_GetHead(arr)
{
   return NumGet(arr + 0, 0, "UInt")
}
LL_SetHead(arr, head)
{
   NumPut(head, arr + 0, 0, "UInt")
}
LL_GetSize(arr)
{
   return NumGet(arr + 0, 4, "UInt")
}
LL_SetSize(arr, size)
{
   NumPut(size, arr + 0, 4, "UInt")
}
LL_Print(arr)
{
   return LLN_Print(LL_GetHead(arr))
}
LL_Add(arr, data, index=-1)
{
   ptr := LL_GetHead(arr)
   prv := 0
   size := LL_GetSize(arr)
   Loop
   {
      if (ptr == 0 || index == A_Index || size + 1 + index + 1 == A_Index)
         break
      prv := ptr
      ptr := LLN_GetNext(ptr)
   }
   if (prv == 0)
      LL_SetHead(arr, LLN_New(data, ptr))
   else
      LLN_SetNext(prv, LLN_New(data, ptr))
   LL_SetSize(arr, LL_GetSize(arr) + 1)
}

LL_Get(arr, index=-1)
{
   ptr := LL_GetHead(arr)
   prv := 0
   size := LL_GetSize(arr)
   Loop
   {
      if (ptr == 0 || index == A_Index || size + index + 1 == A_Index)
         break
      prv := ptr
      ptr := LLN_GetNext(ptr)
   }
   if (ptr == 0)
      return "IndexOutOfBounds"
   return LLN_GetContent(ptr)
}


Tester
Code:
#SingleInstance force

NodeTest:

   node1 := LLN_New("Austria")
   node3 := LLN_New("Canada")
   node2 := LLN_New("Britain", node3)
   
   LLN_SetNext(node1, node2)
   MsgBox,,Node Test, % LLN_Print(node1)
   
   LLN_SetNext(node1, node3)
   MsgBox,,Node Test, % LLN_Print(node1)
   
   LLN_SetContent(node1, "Australlia")
   MsgBox,,Node Test, % LLN_Print(node1)
   
   LLN_SetContent(node2, "Bulgaria")
   LLN_SetContent(node3, "Columbia")
   LLN_SetNext(node1, node2)
   MsgBox,,Node Test, % LLN_Print(node1)
   
ArrTest:
   
   arr1 := LL_New()
   MsgBox, % LL_Print(arr1)
   
   LL_Add(arr1, "Apple")
   MsgBox, % LL_Print(arr1)
   
   LL_Add(arr1, "Banana")
   MsgBox, % LL_Print(arr1)
   
   LL_Add(arr1, "Cantelupe")
   LL_Add(arr1, "Date")
   MsgBox, % LL_Print(arr1)
   
   arr1_4 := LL_Get(arr1)
   arr1_1 := LL_Get(arr1, 1)
   arr1_2 := LL_Get(arr1, 2)
   arr1_3 := LL_Get(arr1, -2)
   arr1p := LL_Print(arr1)
   MsgBox, %arr1_1% -> %arr1_2% -> %arr1_3% -> %arr1_4%`r`n%arr1p%
   
   LL_Add(arr1, "2", 2)
   LL_Add(arr1, "-2", -2)
   MsgBox, % LL_Print(arr1)
   
   arr2 := LL_Copy(arr1)
   LL_Add(arr2, "Final")
   MsgBox, % LL_Print(arr1)
   MsgBox, % LL_Print(arr2)
Back to top
View user's profile Send private message
Lexikos



Joined: 17 Oct 2006
Posts: 7293
Location: Australia

PostPosted: Mon Nov 10, 2008 9:11 am    Post subject: Reply with quote

It may be more efficient and "clean" to allocate memory via DllCall rather than using a global array. For instance,
Code:
LLN_Allocate() {
   return DllCall("GlobalAlloc","uint",0x40,"uint", 64 * 1024 + 8)
}
LLN_Deallocate(pnode) {
   DllCall("GlobalFree","uint",pnode)
}
Also, to copy data, rather than
Code:
   Loop, %size%
      NumPut(Asc(SubStr(data, A_Index, 1)), node + 0, 7 + A_Index, "UChar")
it would be more efficient to use
Code:
DllCall("RtlMoveMemory", "uint", node + 8, "str", data, "uint", size)
This can be used regardless of whether you use GlobalAlloc.
Quote:
(I'd like to say right here that AHK REAAAALLLLLYYYYY needs referenced variables AKA proper dereferencing).
Some interesting things can be done with LowLevel.
Code:
; Initialize __alias and other __functions.
LowLevel_init()

; Store a reference to myvar%n% in ...
NumPut(__getVar(myvar%n%), ...)

; Retrieve a variable reference from ... and assign it the alias myvarref.
__alias(myvarref, NumGet(...))

; Assign a value to myvar%n%.
myvarref := "a value"
Back to top
View user's profile Send private message Visit poster's website
olegbl



Joined: 13 Dec 2006
Posts: 48

PostPosted: Mon Nov 10, 2008 9:35 am    Post subject: Reply with quote

Hmm, this is very interesting.
I haven't familiarized myself with DLLCall yet, so that is definetly a better way. LowLevel is quite an amazing library.. Didn't think such great progress had been made on AHK.
Wow.
Back to top
View user's profile Send private message
Display posts from previous:   
Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions All times are GMT
Page 1 of 1

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group