• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

H (slice)

Status
Not open for further replies.
Level 13
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?
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.
 

Attachments

  • libH.j
    44.3 KB · Views: 45
Level 26
Joined
Aug 18, 2009
Messages
4,097
I did not quite catch the relation between your opening thesis and slicing or why it must be primitive. You can abstract positional collections like e.g. wurst does, passing the pointer (integer) to an object around and that might be backed by an array. No slicing required for first-citizen. The slice is a view/sub structure on the data structure, there might be more like obtained by filtering with custom predicates but yeah, you can basically have them as methods on the data structure class without an extra language feature.
 
Status
Not open for further replies.
Top