0

I am trying to store in a Look-Up-Table(sine) sin(x) values normalized between 0 and 255, with x being mapped from [0; 2Pi] to [0; 255], all integers. However when using trying to access the 0th or 256th index it shows up soemtimes as sine[@]

#! /bin/bash
function load_sin {
    sine=${1}[@]
    PI=3.14159
    for angle in {0..256}
    do  
        sine[$angle]=$(awk "BEGIN{ printf \"%8.0f \", ((sin($angle*($PI/128))*255))}")
    done
}
function sin {
    sine=${1}[@]
    angle=$(($2%256))
    echo "from sin function :sine[$angle] = ${sine[$angle]}"
}

declare -a sine
load_sin sine
for angle in {0..255}
do
    echo -n "from main scope : ${sine[$(($angle%255))]}  "
    sin sine $(($angle))
done

output is the following:

#from main scope :        0   from sin function :sine[0] = sine[@]
#from main scope :        6   from sin function :sine[1] =        6 
#from main scope :       13   from sin function :sine[2] =       13 
#...
#from main scope :      -25   from sin function :sine[252] =      -25 
#from main scope :      -19   from sin function :sine[253] =      -19 
#from main scope :      -13   from sin function :sine[254] =      -13 
#from main scope : sine[@]  from sin function :sine[255] =       -6 

What I'd like to return is in every case: sine[255] = -6 and sine [0] = 0

7
  • 1
    Paste your script into ShellCheck.net Commented Feb 13, 2019 at 21:35
  • ...what do you expect sine=${1}[@] to mean or do? If you want an indirect reference to an array, that's what declare -n is for. Commented Feb 13, 2019 at 21:35
  • ...but then, if this question evolves down to "how do I indirectly reference an array?", there are duplicates we can close it with. Commented Feb 13, 2019 at 21:37
  • Keep in mind that shell variables aren't local unless you make them so. When you set sine=${1}[@]}, you're changing ${sine[0]} because changing the first element of an array is what assigning to that array does. Commented Feb 13, 2019 at 21:39
  • @DavidC.Rankin Okay, but could you please elaborate on what is wrong? I'm a beginner bash scripting and I don't get what is wrong on line 3 and 11 Commented Feb 13, 2019 at 21:39

1 Answer 1

1

This happens because (1) bash variables are all global unless explicitly declared otherwise (so sine inside your function refers to the sine array declared outside of it); and (2) assigning a string to an array changes the first element of that array.

Thus, when you run:

sine=${1}[@]

...that's exactly identical to:

sine[0]=${1}[@]

So when you call load_sin sine, $1 is the string "sine", so the assignment assigns the string sine[@] to the first element of the array sine.


Compare to the following, which does proper indirect references and assignments (albeit as noops when sine is the exact variable name provided):

#!/usr/bin/env bash

load_sin() {
    # if our first argument is not "sine", make sine a reference to the variable thus-named
    [[ $1 != sine ]] && declare -n sine=$1

    while read -r angle value; do
      sine[$angle]=$value
    done < <(awk -v PI=3.14159 -v start=0 -v end=256 '
      BEGIN {
        for(angle=start; angle<end; angle++) {
          printf "%d %8.0f \n", angle, sin(angle*(PI/128))*255
        }
      }')
}

sin() {
    [[ $1 != sine ]] && declare -n sine=$1

    angle=$(($2%256))
    echo "from sin function :sine[$angle] = ${sine[$angle]}"
}

declare -a sine
load_sin sine
for angle in {0..255}
do
    printf '%s' "from main scope : ${sine[$(($angle%255))]}  "
    sin sine $(($angle))
done
Sign up to request clarification or add additional context in comments.

8 Comments

Wow thank you very much I had no idea it was passed as a string, that works perfectly
BTW, we could probably make load_sin much faster by running awk just once and printing all 256 values, instead of calling a separate awk once per value. Let me know if you're interested in that, and I might try to throw it together.
@Soulthym, ...so, I ended up doing that edit anyhow; hope you find it helpful.
Anything that's not built into the shell itself has a significant startup cost just to fork() off a subprocess and then call execve() to replace that forked-off copy of the shell with whatever the external program is (which needs to have its libraries linked and loaded, etc). It's not that big for a one-off, but if you're doing something in a loop -- even just calling /bin/true 256 times is more than half a second on my local box, and awk is larger than true with more dependencies.
well thank you, why not actually, I didn't know awk was that long to invoke
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.