0

I'm working on a matrix/vector class(es) and part of it has operator overloading. One of the odd things is, that instead of returning a new object (in this case vector) it's mutating the vector, AND returning a new one. How do I cleanly return a new instance of my Vector?

here are the constructors:

    private List<double> v = new List<double>();
    private int dims;


    public Vector(double[] a)
    {
        foreach (double d in a)
        {
            v.Add(d);
            dims++;
        }
    }

    public Vector(int dims)
    {
        var a = new double[dims];
        v = a.ToList();
        this.dims = dims;
    }

    private Vector(List<double> a)
    {
        v = a;
        dims = a.Count;
    }

and the operator overloads (just posting addition because all of them have the problem, and have similar construction, so should have identical solutions)

    public static Vector operator + (Vector a, Vector b)
    {
        Vector c = new Vector();
        c = a;
        for (int dim = 0; dim < c.dims; dim++)
        {
            c[dim] += b[dim];
        }
        return c;
    }

EDIT: So, I've changed the class to a struct, and it seems like I still have the issue. Maybe it''s because the variable v is a list, and therefore it's a class, it's still passing in the reference to the list? Maybe I have to use an array instead of a list?

Raxmo
  • 57
  • 5
  • 1
    I recommend that you rename your class to avoid confusing it with [the .net `Vector` struct](https://msdn.microsoft.com/en-us/library/system.windows.vector(v=vs.110).aspx). – Matthew Watson Jul 20 '18 at 09:59
  • I probably should, the old name was Vec I should use that one again – Raxmo Jul 20 '18 at 10:22

5 Answers5

3

The line:

c = a;

is the problem.

Both c and a are then pointing to the same object.

Possible solutions:

1) Change Vector from a class to a struct.

2) Use one of your constructors to create a new Vector object:

public static Vector operator + (Vector a, Vector b)

{
    Vector c = new Vector(a.v);

    for (int dim = 0; dim < c.dims; dim++)
    {
        c[dim] += b[dim];
    }
    return c;
}
SBFrancies
  • 3,987
  • 2
  • 14
  • 37
  • 1
    And how to solve that? – MakePeaceGreatAgain Jul 20 '18 at 09:51
  • @HimBromBeere It's a bit hard to say without more information on what the operator is meant to achieve and the constraints on the operation. What happens if `a` and `b` have different lengths for example, could Vector be converted to a struct? – SBFrancies Jul 20 '18 at 09:54
  • 1
    Then this is a comment not an answer to the question _"How do I cleanly return a new instance of my Vector?"_ – PaulF Jul 20 '18 at 09:55
  • @MatthewWatson this is a custom defined class named Vector and the question specifically says it's a class. – SBFrancies Jul 20 '18 at 09:58
  • @HimBromBeere - fair point, I've updated the answer. – SBFrancies Jul 20 '18 at 09:58
  • @SBFrancies Yes, I thought it was the .Net `Vector` class. I've recommended that the OP changes the name of his class to avoid such confusion. – Matthew Watson Jul 20 '18 at 10:00
  • I am curious as to why changing the vector to a struct would fix the issue. – Raxmo Jul 20 '18 at 10:00
  • 1
    Structs are copied on assignment. https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/structs – Mardukar Jul 20 '18 at 10:03
  • A struct is a value type, when it is assigned or passed to a method the value is copied rather than a reference being sent. See https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/structs for more information. – SBFrancies Jul 20 '18 at 10:03
3

The problem is your c = a which "copy" the address instead of values. Your + operator should look like this:

public static Vector operator + (Vector a, Vector b)
{
    if (a.dims != b.dims)
        throw new WhatEverException();

    Vector c = new Vector(a.dims);
    // c = a
    for (int dim = 0; dim < c.dims; dim++)
    {
        // c[dim] += b[dim];
        c[dim] = a[dim] + b[dim];
    }
    return c;
}

Little plus: you can change your dims member to a readonly property:

private int dims { get { return v.Count; } }

How to test:

    public class Vector
    {
        public char Id { get; set; } // just for debug
        public double this[int i] { get { return Values[i]; } set { Values[i] = value; } }
        private List<double> Values { get; set; }
        public int Dim { get { return Values.Count; } }
        public Vector(double[] values) { Values = values.ToList(); }
        public Vector(int dims) { Values = new double[dims].ToList(); }

        // note this constructor, the one you posted actually copy the whole list object
        public Vector(List<double> values) { Values = new List<double>(values); }

        public static Vector operator +(Vector a, Vector b)
        {
            if (a.Dim != b.Dim)
                throw new Exception();
            Vector c = new Vector(a.Dim) { Id = 'c' };
            for (int i = 0 ; i < c.Dim ; i++)
                c[i] = a[i] + b[i];
            return c;
        }
    }

    static void Main(string[] args)
    {
        Vector a = new Vector(new double[] { 1.42857, 1.42857, 1.42857 }) { Id = 'a' };
        Vector b = new Vector(new double[] { 1.42857, 2.85714, 4.28571 }) { Id = 'b' };
        Vector c = a + b;
        Console.WriteLine(string.Format(">{0} = {1} + {2}", c.Id, a.Id, b.Id));
        Console.WriteLine(string.Format(">{1} + {2} = {0}", c[0], a[0], b[0]));
        Console.WriteLine(string.Format(">{1} + {2} = {0}", c[1], a[1], b[1]));
        Console.WriteLine(string.Format(">{1} + {2} = {0}", c[2], a[2], b[2]));
        Console.ReadKey();
    }

Result:

>c = a + b
>1.42857 + 1.42857 = 2.85714
>1.42857 + 2.85714 = 4.28571
>1.42857 + 4.28571 = 5.71428
Maxime Recuerda
  • 476
  • 3
  • 14
  • would changing the dims to a readonly prevent me from setting properly at declaration? because I have the Vector class currently set up as an indexable class, and not sure if the Add() method will operate properly if I make it readonly – Raxmo Jul 20 '18 at 10:03
  • I've actually tried to have c be constructed in the function, and it STILL references the vector a, I'll try re-writing it as a struct and see if that works. – Raxmo Jul 20 '18 at 10:24
  • A readonly property is like a single method. You wouldn't need to set your property at all, as it would always calculate itself! I'll do some tries, but I don't understand how it can still reference `a`, as we never use it, but only its values. – Maxime Recuerda Jul 20 '18 at 13:29
  • I've editted with the full code you want/need. There is no problem for me, I even added a 'Id' property to check which instance I'm getting. – Maxime Recuerda Jul 20 '18 at 13:57
1

You should either copy all vector a values to Vector c, or change Vector to struct instead of class.

Doing a = c, makes c reference a, therefore all modifications to c is also applied to a. It happens because Vector is a class and it is passed around as a reference rather than value.

One way to work around this is to loop through all a values and add them to c. Or, better, you have constructor, that does that for you, so your end operator override should look like

public static Vector operator + (Vector a, Vector b)
{
    // Will work, because you are still inside Vector class and you can access all private members.
    Vector c = new Vector(a.v);  

    for (int dim = 0; dim < c.dims; dim++)
        c[dim] += b[dim];

    return c;
}
tsvedas
  • 1,049
  • 7
  • 18
1

You can use Zip to add elements of two lists. Also, you could use AddRange instead of loops.

class Vector
{
    private List<double> v = new List<double>();
    private int dims;

    public Vector(double[] a)
    {
        v.AddRange(a);
    }

    public Vector(int dims)
    {
        var a = new double[dims];
        v = a.ToList();
        this.dims = dims;
    }

    private Vector(List<double> a)
    {
        v = new List<double>(a);
        dims = a.Count;
    }

    public static Vector operator +(Vector a, Vector b)
    {
        var ls = a.v.Zip(b.v, (x, y) => x + y).ToList();
        return new Vector(ls);
    }
}
L_J
  • 2,351
  • 10
  • 23
  • 28
0

allright, I've finaly worked something out. it's a tad messy but it works. and I have no idea why I have to jump through so many hoops in order to get this thing working, but here's my solution:

    public static Vec operator + (Vec a, Vec b)
    {
        Vec c = new Vec();
        c.v = new List<double>();
        for (int dim = 0; dim < a.Dims; dim++)
        {
            c.v.Add(a[dim] + b[dim]);
        }
        return c;
    }

I'm unsure if there is a more elegant way to do this, but hey, it works. I've also made it a struct rather than a class.

Raxmo
  • 57
  • 5