Deriving equality for Julia structs with mutable members - struct

In the Julia code below, we have equality not working for T{BigInt}, due to BigInt being a mutable struct. == is explicitly defined by BigInt for themselves, however.
julia> struct T{X}
x :: X
end
julia> T{Int64}(1) == T{Int64}(1), T{Int64}(1) === T{Int64}(1)
(true, true)
julia> T{BigInt}(1) == T{BigInt}(1), T{BigInt}(1) === T{BigInt}(1)
(false, false)
julia> T{BigInt}(1).x == T{BigInt}(1).x, T{BigInt}(1).x === T{BigInt}(1).x
(true, false)
Is there a way to either:
autogenerate an == for these sorts of structs which just recurses == on each field,
or have immutable versions of mutable structs as members (as with const in C++), other than using an immutable equivalent to BigInt?
My goal is to avoid boilerplate in a package with a large number of such structs.

This should work:
function my_equals(a::S, b::S) where S
for name in fieldnames(S)
if getfield(a, name) != getfield(b, name)
return false
end
end
return true
end
I tried overloading == via
import Base.==
function ==(a::S, b::S) where S
for name in fieldnames(S)
if getfield(a, name) != getfield(b, name)
return false
end
end
return true
end
That has the intended behavior, but (unsurprisingly) seems to break things (i.e. you can't even call exit() after redefining == like that).
If you want to use ==, then you could have all your custom structs inherit from some abstract type, like this:
abstract type Z end
struct T{X} <: Z
x::X
end
struct S{X} <: Z
x::X
y::X
end
import Base.==
function ==(a::V, b::V) where V <: Z
for name in fieldnames(V)
if getfield(a, name) != getfield(b, name)
return false
end
end
return true
end
Then you can use
julia> T{BigInt}(1) == T{BigInt}(1)
true
julia> S{BigInt}(2, 5) == S{BigInt}(2, 5)
true
julia> T{BigInt}(1) == T{BigInt}(2)
false
julia> S{BigInt}(2, 5) == S{BigInt}(2, 3)
false
That doesn't interfere with existing ==.

Related

how to return a new instance in julia

how can I return a new instance from a struct within a function in Julia?
function generateField(type, default, description, max, min)
return Field{type}(default,description,min,max)
end
I have a struct called Field. I just used Java logic hier and it did not work for sure :)
struct Field{T}
type::T
default::T
description::String
min::T
max::T
function Field{T}(description,min,max) where {T}
new{T}(description, min, max)
end
end
Just use deepcopy(obj) - it will make a copy of your struct object (assuming this is what you need):
julia> mutable struct Field2{T<:Real}
a::T
b::Vector{Float64}
end
julia> x = Field2{Int}(5,[4.5])
Field2{Int64}(5, [4.5])
julia> y = deepcopy(x)
Field2{Int64}(5, [4.5])
julia> y == x
false
Note that x and y point to different variables and hence x == y yields false. To compare by value one would need to iterate over all struct fields.
For this struct a function generating it could be defined as :
function f(a::T, b::Vector{Float64}) where T <: Real
return Field2{T}(a,b)
end
And used as:
julia> f(1//2, [3.5])
Field2{Rational{Int64}}(1//2, [3.5])
Finally, note that the function generating an object can have the same name as its type - in that case you are generating new constructor:
julia> Field2(a::T) where T<:Real = Field2{T}(a,Int[]);
julia> Field2(2)
Field2{Int64}(2, Float64[])

Comparing Lists of Strings in Scala

I know lists are immutable but I'm still confused on how I would go about this. I have two lists of strings - For example:
var list1: List[String] = List("M", "XW1", "HJ", "K")
var list2: List[String] = List("M", "XW4", "K", "YN")
I want to loop through these lists and see if the elements match. If it doesn't, the program would immediately return false. If it is a match, it will continue to iterate until it finds an element that begins with X. If it is indeed an X, I want to return true regardless of whether the number is the same or not.
Problem I'm having is that currently I have a conditional stating that if the two elements do not match, return false immediately. This is a problem because obviously XW1 and XW4 are not the same and it will return false. How can I bypass this and determine that it is a match to my eyes regardless of the number?
I also have a counter a two length variables to account for the fact the lists may be of differing length. My counter goes up to the shortest list: for (x <- 0 to (c-1)) (c being the counter).
You want to use zipAll & forall.
def compareLists(l1: List[String], l2: List[String]): Boolean =
l1.zipAll(l2, "", "").forall {
case (x, y) =>
(x == y) || (x.startsWith("X") && y.startsWith("X"))
}
Note that I am assuming an empty string will always be different than any other element.
If I understand your requirement correctly, to be considered a match, 1) each element in the same position of the two lists being simultaneously iterated must be the same except when both start with X (in which case it should return true without comparing any further), and 2) both lists must be of the same size.
If that's correct, I would recommend using a simple recursive function like below:
def compareLists(ls1: List[String], ls2: List[String]): Boolean = (ls1, ls2) match {
case (Nil, Nil) =>
true
case (h1 :: t1, h2 :: t2) =>
if (h1.startsWith("X") && h2.startsWith("X"))
true // short-circuiting
else
if (h1 != h2)
false
else
compareLists(t1, t2)
case _ =>
false
}
Based on your comment that, result should be true for lists given in question, you could do something like this:
val list1: List[String] = List("M", "XW1", "HJ", "K")
val list2: List[String] = List("M", "XW4", "K", "YN")
val (matched, unmatched) = list1.zipAll(list2, "", "").partition { case (x, y) => x == y }
val result = unmatched match {
case Nil => true
case (x, y) :: _ => (x.startsWith("X") && y.startsWith("X"))
}
You could also use cats foldM to iterate through the lists and terminate early if there is either (a) a mismatch, or (b) two elements that begin with 'X':
import cats.implicits._
val list1: List[String] = List("M", "XW1", "HJ", "K")
val list2: List[String] = List("M", "XW4", "K", "YN")
list1.zip(list2).foldM(()){
case (_, (s1, s2)) if s1 == s2 => ().asRight
case (_, (s1, s2)) if s1.startsWith("X") && s2.startsWith("X") => true.asLeft
case _ => false.asLeft
}.left.getOrElse(false)

I want to know how can is shorten this code and make it look more proper

I want to know if the code I wrote can be shortened further, I was practicing and I came up to a task which asks you to return a boolean value, this is what the question says:
Given two strings, return True if either of the strings appears at the
very end of the other string, ignoring upper/lower case differences
(in other words, the computation should not be "case sensitive").
Note: s.lower() returns the lowercase version of a string.
def end_other(a, b):
x = len(b)
n = a[-x:]
y = len(a)
m = b[-y:]
if b.lower() == n.lower() or a.lower() == m.lower() :
return True
else:
return False
The Code is working properly but I wondered if it can be shortened more so it looks good.
You can write it like this:
def end_other(a, b):
n = a[-len(b):]
m = b[-len(a):]
return b.lower() == n.lower() or a.lower == m.lower()
I removed variables x and y because they are used just one time and then I also remove the if-else statement because it's unnecessary, in fact you can just return the result of the comparison instead of checking it's result and returning it a second time.

number as an object, or storing properties of a number

in designing an algebraic equation modelling system, I had this dilemma: we cannot associate properties to a number, if I turn the number to a table with a field "value" for example, I can overload arithmetic operators, but not the logic operator since that only works when both operands have same metatable, while my users will compare "x" with numbers frequently.
For example, here is a minimal equation solver system:
x = 0
y = 0
eq1 = {function() return 2*x + 3*y end, rhs = 1 }
eq2 = {function() return 3*x + 2*y end, rhs = 2 }
p = {{x,y},{eq1, eq2}}
solve(p)
The "solve()" will process table "p" to get all coefficients of the equation system and rhs. However, it is essential, a user can associate properties to "x" and "y", for example, lower bound, upper bound. I tries using table,
x = {val=0, lb=0, ub=3}
y = {val=1,lb=3,ub=5}
....
and write metamethods for "x" and "y" such that arithmetic operating will act on x.val and y.val. However, in a scripting environment, we also need to compare "x" with numbers, i.e., "if x>0 then ...". And I stuck here. An ugly solution is to ask users to use x.val, y.val everywhere in modelling the equation and scripting. Does anyone here has similar need to associate properties to a number, and the number can still be used in arithmetic/logic operations?
Something like this could work:
x = {val = 10}
mt = {}
mt.__lt = function (op1, op2)
if (type(op1) == 'table') then a = op1.val else a = op1 end
if (type(op2) == 'table') then b = op2.val else b = op2 end
return a < b
end
setmetatable(x, mt)
print(x < 5) -- prints false
print(x < 15) -- prints true
print(x < x) -- prints false
print(5 < x) -- prints true
Of course, you would write similar methods for the other operators (__add, __mul, __eq and so on).
If you'd rather not use type()/reflection, you can use an even dirtier trick that takes advantage of the fact that unary minus is well, unary:
mt = {}
mt.__unm = function (num) return -(num.val) end
mt.__lt = function (a, b) return -(-a) < -(-b) end
This is rather simple if you have access to the debug library, do you?
debug.setmetatable(0, meta)
meta will be the metatable of ALL numbers. This will solve your logical overloading problem.
However if you would prefer assigning properties to numbers, there is a way you could do this, I wrote a quick example on how one would do so:
local number_props = {
{val="hi"},
{val="hi2"}
}
debug.setmetatable(0,{__index=function(self,k)return number_props[self][k]end})
print((1).val, (2).val)

How to manipulate the value of variables based on an array of symbols in Julia?

This is somewhat related to David's question here. I'm interested in looping over an array of symbols (pointing to objects) and performing an operation on each step of the loop. For example, suppose I want to write a function that looks at a bunch of variables and converts any scalars to one-element arrays.
widget = "a scalar"
dohickey = ["item 1", "item 2"]
gizmo = "another scalar"
theVariables = [:widget, :dohickey, :gizmo]
for thisVariable in theVariables
if !(typeof(eval(thisVariable)) <: Array)
println("Found a scalar")
#Make it a vector here
end
end
Ideally at the end of this we would have the following
widget = ["a scalar"]
dohickey = ["item 1", "item 2"]
gizmo = ["another scalar"]
This is a fairly lame example, but how can this be done? I thought I should be able to use something like
:($thisVariable) = [:($thisVariable)]
but I can't get it to work.
Edit: DSM's solution below works for the case described above, but in my actual usage I want to do this inside a function. If I define
function getsort(; arga="", argb="", argc="")
filterSpecs = [:arga, :argb, :argc]
for ts in filterSpecs
if !(typeof($ts) <: Array)
#nothing here
end
end
end
and then call getsort() it throws
error compiling getsort: error compiling __getsort#31__: syntax: prefix $ in non-quoted expression
I'm clearly missing something about metaprogramming magic.
This is more a followup to #DSM's answer but writing code in comments is hard. A word of caution: eval evaluates in the global scope which can lead to strange things
julia> a = 3
3
julia> function f(a)
eval(:(a = 1))
println(a)
end
f (generic function with 1 method)
julia> f(a)
3
julia> a
1
From the code generation section of the docs, I think all you need is
#eval $thisVariable = [$thisVariable]
For example:
widget = "a scalar"
dohickey = ["item 1", "item 2"]
gizmo = "another scalar"
theVariables = [:widget, :dohickey, :gizmo]
for thisVariable in theVariables
if !(typeof(eval(thisVariable)) <: Array)
println("Found a scalar")
#eval $thisVariable = [$thisVariable]
end
end
which gives me
Found a scalar
Found a scalar
julia> widget
1-element Array{ASCIIString,1}:
"a scalar"
julia> dohickey
2-element Array{ASCIIString,1}:
"item 1"
"item 2"
julia> gizmo
1-element Array{ASCIIString,1}:
"another scalar"
Are you sure you need a macro here? If you're looking to just handle your function inputs you can do something like:
julia> function newDictOfArrays(d)
wrapped=false
for (k,v) in d
if !(typeof(v) <: AbstractArray)
wrapped = true
d[k] = [v]
end
end
(wrapped, d)
end
newDictOfArrays (generic function with 1 method)
julia> function f(; kwargs...)
(wrapped, d) = newDictOfArrays(Dict(kwargs))
if wrapped
return f(;d...)
end
println("continue knowing all keyword args have been wrapped: $kwargs")
end
f (generic function with 1 method)
julia> f(a=1, b=[2])
continue knowing all keyword args have been wrapped: Any[(:a,[1]),(:b,[2])]
julia> f(a=[1], b=[2])
continue knowing all keyword args have been wrapped: Any[(:a,[1]),(:b,[2])]
newDictOfArrays checks all members of a dictionary for values that are not subtypes of AbstractArray, and overrides that item with a wrapped value. In f, if anything was wrapped, it re-calls the same function again with the new dictionary passed as keyword arguments. No macros, eval, etc, although you could consider writing a macro that injects the code into f automatically if need this frequently.

Resources