- Joined
- Nov 7, 2014
- Messages
- 571
H (slice) or: How I Learned to Stop Worrying and Love the Hashtable
The thing that I find most annoying about scripting in [v]Jass, is the fact that arrays are both limited in size (8K, 32K in newer patches?) and, even worse, they are not first-class entities/values.
This means that they can't be passed/returned to/from functions nor stored in struct fields/local/global variables. In my opinion if someone doesn't have access to a first-class "array" primitive, they are hurting themselves needlessly.
What would using a first-class array primitive look like in [v]Jass?
What it looks like in "reality" (by reality, I mean the silly implementation of a first-class array primitive that I could concoct):
You should read 'H_int' as 'slice of integers'. The letter 'H' in my opinion looks somewhat like '[]' (if you stare at it long enough). We of course add the '_' after 'H' otherwise 'Hint' would not read as a 'slice of integers' but as a 'Hint' (a small piece of practical information or advice).
The idea of slices is borrowed from here.
Now then, having such a primitive, we can begin to write functions that can be reused and composed:
I have my doubts that these silly examples would convince you, but in any case, I think having "solid building blocks" on top of which to build more elaborate things is the way to go.
The thing that I find most annoying about scripting in [v]Jass, is the fact that arrays are both limited in size (8K, 32K in newer patches?) and, even worse, they are not first-class entities/values.
This means that they can't be passed/returned to/from functions nor stored in struct fields/local/global variables. In my opinion if someone doesn't have access to a first-class "array" primitive, they are hurting themselves needlessly.
What would using a first-class array primitive look like in [v]Jass?
JASS:
function get_integers_up_to takes integer n returns []integer
local []integer a // 'a' starts up empty
local integer i = 0
loop
exitwhen i >= n // 'a' can store an unlimited number of elements (not just 8K/32K)
call a.push(i)
set i = i + 1
endloop
return a // 'a' returned from function
endfunction
function add1 takes []integer a returns nothing // 'a' passed as a parameter
// looping over all the elements
local integer i = 0
loop
exitwhen i >= a.len
set a[i] = a[i] + 1
set i = i + 1
endloop
endfunction
function example takes nothing returns nothing
local []integer a = get_integers_up_to(3) // a == [0, 1, 2]
call add1(a) // a == [1, 2, 3]
call add1(a[1 .. a.len]) // a == [1, 3, 4]
// this is a scripting language, we don't have to allocate/free memory explicitly
endfunction
What it looks like in "reality" (by reality, I mean the silly implementation of a first-class array primitive that I could concoct):
JASS:
function get_integers_up_to takes integer n returns H_int
local H_int s = H_int(0) // 's' starts up empty, i.e 's' is the empty slice
local integer i = 0
loop
exitwhen i >= n // 's' can store an unlimited number of elements because it stores its data in a hashtable
set s = s.push(i) // Note: 's' is reassigned because it started up as the empty slice
set i = i + 1
endloop
return s // 's' returned from function
endfunction
function add1 takes H_int s returns nothing // 's' passed as a parameter
// looping over all the elements
local integer i = 0
loop
exitwhen i >= s.len
set s[i] = s[i] + 1
set i = i + 1
endloop
endfunction
function example takes nothing returns nothing
local H_int s = get_integers_up_to(3) // s == [0, 1, 2]
call add1(s) // s == [1, 2, 3]
call add1(s.H(1, s.len)) // s == [1, 3, 4]
// this is a scripting language in which we have to allocate/free memory explicitly
call s.destroy()
endfunction
You should read 'H_int' as 'slice of integers'. The letter 'H' in my opinion looks somewhat like '[]' (if you stare at it long enough). We of course add the '_' after 'H' otherwise 'Hint' would not read as a 'slice of integers' but as a 'Hint' (a small piece of practical information or advice).
The idea of slices is borrowed from here.
Now then, having such a primitive, we can begin to write functions that can be reused and composed:
JASS:
function example takes nothing returns nothing
local string words_str = "there are 69105 leaves here"
local H_string words = libH_str_split(" ", words_str)
local H_string uc_words = H_string(0)
local integer i
set i = 0
loop
exitwhen i >= words.len
if words[i] == "0" or 0 != S2I(words[i]) then
// filter out the number(s)
else
set uc_words = uc_words.push(StringCase(words[i], /*upper:*/ true))
endif
set i = i + 1
endloop
call BJDebugMsg(H_string_join(" ", uc_words)) // THERE ARE LEAVES HERE
call words.destroy()
call uc_words.destroy()
endfunction
JASS:
globals
H_unit units = H_unit(0)
endglobals
function sort_units_by_x_coord_asc takes nothing returns nothing
local real a = GetUnitX(unit_a)
local real b = GetUnitX(unit_b)
if a < b then
set cmp_result = -1
elseif a > b then
set cmp_result = 1
else
set cmp_result = 0
endif
endfunction
function sort_units takes nothing returns nothing
call sort_H_unit_insertion(units, function sort_units_by_x_coord_asc)
endfunction
function print_units takes nothing returns nothing
local integer i
local string s
local unit u
call sort_units()
call ClearTextMessages()
set i = 0
loop
exitwhen i >= units.len
set u = units[i]
set s = GetUnitName(u) + " at " + R2SW(GetUnitX(u), 1, 2)
call BJDebugMsg(s)
set i = i + 1
endloop
endfunction
function example takes nothing returns nothing
local player red = Player(0)
set units = units.push(CreateUnit(red, 'hfoo', 0.0, 0.0, 270.0))
set units = units.push(CreateUnit(red, 'hkni', -256.0, 0.0, 270.0))
set units = units.push(CreateUnit(red, 'ogru', 256.0, 0.0, 270.0))
call TimerStart(CreateTimer(), 1.0, true, function print_units)
endfunction
I have my doubts that these silly examples would convince you, but in any case, I think having "solid building blocks" on top of which to build more elaborate things is the way to go.