Someone please help me understand this. I have the following code below. I am trying to append index[i]-1 to an empty array. However I am getting this error: "BoundsError: attempt to access 0-element Array{Any,1} at index [1]" :
sample_size_array = [9,5,6,9,2,6,9]
n_minus_1 = []
array_length = length(sample_size_array)
for i in 1:array_length
n_minus_1[i].append(sample_size_array[i] -1)
end
println(n_minus_1)
If Julia does not understand array[0] then why is i starting at 0 and not at 1?
Your code has two problems:
in the first iteration you are trying to access n_minus_1 array at index 1 while this array is still empty (has 0 length) - this throws you an error;
in Julia you do not invoke methods using a . (this symbol is used for different purposes - in this case it is parsed as field access and also would throw an error later)
To solve both those problems use push! function which appends an element at the end of an array. The code could look like this:
sample_size_array = [9,5,6,9,2,6,9]
n_minus_1 = []
array_length = length(sample_size_array)
for i in 1:array_length
push!(n_minus_1, sample_size_array[i]-1)
end
println(n_minus_1)
However in this case the whole operation can be written even simpler as:
n_minus_1 = sample_size_array .- 1
and you do not need any loop (and here you see another use of . in Julia - in this case we use it to signal that we want to subtract 1 from every element of sample_size_array).
Related
I'm very new to Julia, and I'm trying to just pass an array of numbers into a function and count the number of zeros in it. I keep getting the error:
ERROR: UndefVarError: array not defined
I really don't understand what I am doing wrong, so I'm sorry if this seems like such an easy task that I can't do.
function number_of_zeros(lst::array[])
count = 0
for e in lst
if e == 0
count + 1
end
end
println(count)
end
lst = [0,1,2,3,0,4]
number_of_zeros(lst)
There are two issues with your function definition:
As noted in Shayan's answer and Dan's comment, the array type in Julia is called Array (capitalized) rather than array. To see:
julia> array
ERROR: UndefVarError: array not defined
julia> Array
Array
Empty square brackets are used to instantiate an array, and if preceded by a type, they specifically instantiate an array holding objects of that type:
julia> x = Int[]
Int64[]
julia> push!(x, 3); x
1-element Vector{Int64}:
3
julia> push!(x, "test"); x
ERROR: MethodError: Cannot `convert` an object of type String to an object of type Int64
Thus when you do Array[] you are actually instantiating an empty vector of Arrays:
julia> y = Array[]
Array[]
julia> push!(y, rand(2)); y
1-element Vector{Array}:
[0.10298669573927233, 0.04327245960128345]
Now it is important to note that there's a difference between a type and an object of a type, and if you want to restrict the types of input arguments to your functions, you want to do this by specifying the type that the function should accept, not an instance of this type. To see this, consider what would happen if you had fixed your array typo and passed an Array[] instead:
julia> f(x::Array[])
ERROR: TypeError: in typeassert, expected Type, got a value of type Vector{Array}
Here Julia complains that you have provided a value of the type Vector{Array} in the type annotation, when I should have provided a type.
More generally though, you should think about why you are adding any type restrictions to your functions. If you define a function without any input types, Julia will still compile a method instance specialised for the type of input provided when first call the function, and therefore generate (most of the time) machine code that is optimal with respect to the specific types passed.
That is, there is no difference between
number_of_zeros(lst::Vector{Int64})
and
number_of_zeros(lst)
in terms of runtime performance when the second definition is called with an argument of type Vector{Int64}. Some people still like type annotations as a form of error check, but you also need to consider that adding type annotations makes your methods less generic and will often restrict you from using them in combination with code other people have written. The most common example of this are Julia's excellent autodiff capabilities - they rely on running your code with dual numbers, which are a specific numerical type enabling automatic differentiation. If you strictly type your functions as suggested (Vector{Int}) you preclude your functions from being automatically differentiated in this way.
Finally just a note of caution about the Array type - Julia's array's can be multidimensional, which means that Array{Int} is not a concrete type:
julia> isconcretetype(Array{Int})
false
to make it concrete, the dimensionality of the array has to be provided:
julia> isconcretetype(Array{Int, 1})
true
First, it might be better to avoid variable names similar to function names. count is a built-in function of Julia. So if you want to use the count function in the number_of_zeros function, you will undoubtedly face a problem.
Second, consider returning the value instead of printing it (Although you didn't write the print function in the correct place).
Third, You can update the value by += not just a +!
Last but not least, Types in Julia are constantly introduced with the first capital letter! So we don't have an array standard type. It's an Array.
Here is the correction of your code.
function number_of_zeros(lst::Array{Int64})
counter = 0
for e in lst
if e == 0
counter += 1
end
end
return counter
end
lst = [0,1,2,3,0,4]
number_of_zeros(lst)
would result in 2.
Additional explanation
First, it might be better to avoid variable names similar to function names. count is a built-in function of Julia. So if you want to use the count function in the number_of_zeros function, you will undoubtedly face a problem.
Check this example:
function number_of_zeros(lst::Array{Int64})
count = 0
for e in lst
if e == 0
count += 1
end
end
return count, count(==(1), lst)
end
number_of_zeros(lst)
This code will lead to this error:
ERROR: MethodError: objects of type Int64 are not callable
Maybe you forgot to use an operator such as *, ^, %, / etc. ?
Stacktrace:
[1] number_of_zeros(lst::Vector{Int64})
# Main \t.jl:10
[2] top-level scope
# \t.jl:16
Because I overwrote the count variable on the count function! It's possible to avoid such problems by calling the function from its module:
function number_of_zeros(lst::Array{Int64})
count = 0
for e in lst
if e == 0
count += 1
end
end
return count, Base.count(==(1), lst)
The point is I used Base.count, then the compiler knows which count I mean by Base.count.
Suppose I have the following code which has two nested while loops.
struct Parameters
maxIter1::Float64
maxIter2::Float64
tolerance1::Float64
tolerance2::Float64
end
mutable struct Guess
x1::Float64
x2::Float64
end
function solveModel(par::Parameters,initGuess::Guess)
iterate1 = 0
error1 = 0
guess = initGuess
while (par.iterate1 < par.maxIter1 && error1 > par.tolerance1)
iterate1 += 1
iterate2 = 0
error2 = 0
guess.x2 = initGuess.x2
while (iterate2 < par.maxIter2 && error2 > par.tolerance2)
iterate2 += 1
z2 = solveInnerProblem(par,guess)
newGuess = update2(par,guess,z2)
error2 = computeError2(newGuess,guess)
guess = newGuess
end
guess = newGuess
end
end
I get an error message,
Note: the reference to the line number is erroenous - line 294 of my code contains no mention whatsoever of newGuess.
The error message goes away if I comment out the line
guess = newGuess
In the outer loop (last line before the final two end lines in the code snippet). I'm quite confused as to why this is happening. The variable newGuess is clearly defined, but Julia says it is not defined...
newGuess is a local variable, which means that it is defined in a localized part of the program rather than all the program. In the case of a local variable defined within a loop like a while statement, the variable is undefined outside the while loop within which it is defined, which is the inner while loop of your function. So the "not defined" error is because the program is trying to access the variable outside of its local scope-- it was defined before, but not when the error occurs.
You may need to define newGuess higher up, within the function, but before the inner while statement.
I am trying to learn Erlang and I am working on the practice problems Erlang has on the site. One of them is:
Write the function time:swedish_date() which returns a string containing the date in swedish YYMMDD format:
time:swedish_date()
"080901"
My function:
-module(demo).
-export([swedish_date/0]).
swedish_date() ->
[YYYY,MM,DD] = tuple_to_list(date()),
string:substr((integer_to_list(YYYY, 3,4)++pad_string(integer_to_list(MM))++pad_string(integer_to_list(DD)).
pad_string(String) ->
if
length(String) == 1 -> '0' ++ String;
true -> String
end.
I'm getting the following errors when compiled.
demo.erl:6: syntax error before: '.'
demo.erl:2: function swedish_date/0 undefined
demo.erl:9: Warning: function pad_string/1 is unused
error
How do I fix this?
After fixing your compilation errors, you're still facing runtime errors. Since you're trying to learn Erlang, it's instructive to look at your approach and see if it can be improved, and fix those runtime errors along the way.
First let's look at swedish_date/0:
swedish_date() ->
[YYYY,MM,DD] = tuple_to_list(date()),
Why convert the list to a tuple? Since you use the list elements individually and never use the list as a whole, the conversion serves no purpose. You can instead just pattern-match the returned tuple:
{YYYY,MM,DD} = date(),
Next, you're calling string:substr/1, which doesn't exist:
string:substr((integer_to_list(YYYY,3,4) ++
pad_string(integer_to_list(MM)) ++
pad_string(integer_to_list(DD))).
The string:substr/2,3 functions both take a starting position, and the 3-arity version also takes a length. You don't need either, and can avoid string:substr entirely and instead just return the assembled string:
integer_to_list(YYYY,3,4) ++
pad_string(integer_to_list(MM)) ++
pad_string(integer_to_list(DD)).
Whoops, this is still not right: there is no such function integer_to_list/3, so just replace that first call with integer_to_list/1:
integer_to_list(YYYY) ++
pad_string(integer_to_list(MM)) ++
pad_string(integer_to_list(DD)).
Next, let's look at pad_string/1:
pad_string(String) ->
if
length(String) == 1 -> '0' ++ String;
true -> String
end.
There's a runtime error here because '0' is an atom and you're attempting to append String, which is a list, to it. The error looks like this:
** exception error: bad argument
in operator ++/2
called as '0' ++ "8"
Instead of just fixing that directly, let's consider what pad_string/1 does: it adds a leading 0 character if the string is a single digit. Instead of using if to check for this condition — if isn't used that often in Erlang code — use pattern matching:
pad_string([D]) ->
[$0,D];
pad_string(S) ->
S.
The first clause matches a single-element list, and returns a new list with the element D preceded with $0, which is the character constant for the character 0. The second clause matches all other arguments and just returns whatever is passed in.
Here's the full version with all changes:
-module(demo).
-export([swedish_date/0]).
swedish_date() ->
{YYYY,MM,DD} = date(),
integer_to_list(YYYY) ++
pad_string(integer_to_list(MM)) ++
pad_string(integer_to_list(DD)).
pad_string([D]) ->
[$0,D];
pad_string(S) ->
S.
But a simpler approach would be to use the io_lib:format/2 function to just format the desired string directly:
swedish_date() ->
io_lib:format("~w~2..0w~2..0w", tuple_to_list(date())).
First, note that we're back to calling tuple_to_list(date()). This is because the second argument for io_lib:format/2 must be a list. Its first argument is a format string, which in our case says to expect three arguments, formatting each as an Erlang term, and formatting the 2nd and 3rd arguments with a width of 2 and 0-padded.
But there's still one more step to address, because if we run the io_lib:format/2 version we get:
1> demo:swedish_date().
["2015",["0",56],"29"]
Whoa, what's that? It's simply a deep list, where each element of the list is itself a list. To get the format we want, we can flatten that list:
swedish_date() ->
lists:flatten(io_lib:format("~w~2..0w~2..0w", tuple_to_list(date()))).
Executing this version gives us what we want:
2> demo:swedish_date().
"20150829"
Find the final full version of the code below.
-module(demo).
-export([swedish_date/0]).
swedish_date() ->
lists:flatten(io_lib:format("~w~2..0w~2..0w", tuple_to_list(date()))).
UPDATE: #Pascal comments that the year should be printed as 2 digits rather than 4. We can achieve this by passing the date list through a list comprehension:
swedish_date() ->
DateVals = [D rem 100 || D <- tuple_to_list(date())],
lists:flatten(io_lib:format("~w~2..0w~2..0w", DateVals)).
This applies the rem remainder operator to each of the list elements returned by tuple_to_list(date()). The operation is needless for month and day but I think it's cleaner than extracting the year and processing it individually. The result:
3> demo:swedish_date().
"150829"
There are a few issues here:
You are missing a parenthesis at the end of line 6.
You are trying to call integer_to_list/3 when Erlang only defines integer_to_list/1,2.
This will work:
-module(demo).
-export([swedish_date/0]).
swedish_date() ->
[YYYY,MM,DD] = tuple_to_list(date()),
string:substr(
integer_to_list(YYYY) ++
pad_string(integer_to_list(MM)) ++
pad_string(integer_to_list(DD))
).
pad_string(String) ->
if
length(String) == 1 -> '0' ++ String;
true -> String
end.
In addition to the parenthesis error on line 6, you also have an error on line 10 where yo use the form '0' instead of "0", so you define an atom rather than a string.
I understand you are doing this for educational purpose, but I encourage you to dig into erlang libraries, it is something you will have to do. For a common problem like this, it already exists function that help you:
swedish_date() ->
{YYYY,MM,DD} = date(), % not useful to transform into list
lists:flatten(io_lib:format("~2.10.0B~2.10.0B~2.10.0B",[YYYY rem 100,MM,DD])).
% ~X.Y.ZB means: uses format integer in base Y, print X characters, uses Z for padding
I got a strange kind of issue. When I define this table:
function test()
a = Float32[0.3010299957,0.3010299957,-0.3010299957,0.3010299957,0.3010299957]
return a[1]*a[3]
end
It's fine. After call test() i got correct output. But when I define this one, there is an error ErrorException("−3 not defined"):
function test()
a = Float32[2.718281828, −3.141592654 , 1.414213562 , 0.5772156649 , 0.3010299957]
return a[1]*a[2]
end
You're using two different dashes: - (HYPHEN-MINUS) in the first, and − (MINUS SIGN) in the second. Issues like these often happen when you copy text from a formatted source (web page, document, etc.) You want to use HYPHEN-MINUS:
julia> -1 # hyphen-minus
-1
julia> −1 # minus sign
ERROR: syntax: invalid character "−"
IDL beginner here! Let's say I have two procedures, PRO1 and PRO2. If I receive a command line argument in PRO2, how can I give the argument value to a variable in PRO1?
I have previously tried to make an object reference ,'My', to PRO1, but I receive a syntax error on line 6.
PRO PRO2
opts = ob_new('mg_options)
opts.addOption, 'value', 'v'
opts.parseArgs,error_message = errorMsg
My = obj_new('PRO1')
My.A=opts.get('value')
END
For reference, I attempted to follow these instructions for receiving command line arguments: http://michaelgalloy.com/2009/05/11/command-line-options-for-your-idl-program.html
I had something else here, but I think your example above is actually what you want to avoid, yes? I'm not sure how it ends up being all that different, but if you want to make your procedure an object, you'll have to define an actual object (see here) and create methods for it containing your code functionality. Here's something close-ish.
In a file called pro1__define.pro:
function pro1::Init
self.A = 0L
return, 1
end
pro pro1::process, in_val
self.A = in_val
print, self.A
end
pro pro1__define
struct = {pro1, A:0L}
end
Then in pro2 you would do something like
arg = 2
pro1_obj = pro1()
pro1_obj->process, arg
Depending on which version of IDL you are using you may have to modify the initialization line to the obj_new() syntax.