Generic as parameter, what am I doing wrong? - c#

I am learning basic sort algorithms from some source in github. Now, I am trying to make a method that can be used for all sorter instance but I am facing error CS1503 which says that I can not convert int[] into T[]. The intellisense recommends me to add new method but I don't want to add it. Here is my code:
class Program
{
static void Main(string[] args)
{
test<int>(new BubbleSorter<int>(), new IntComparer());
}
static void test<T>(ISorter<T> sorter, IComparer<T> intComparer)
{
var (correctArray, testArray) = RandomHelper.GetArrays(10); //Generates random arrays
sorter.Sort(testArray, intComparer); //This line error
Array.Sort(correctArray);
Console.WriteLine(string.Join(",", testArray));
Console.WriteLine(string.Join(",", correctArray));
}
}
public interface ISorter<T>
{
void Sort(T[] array, IComparer<T> comparer);
}
class BubbleSorter<T> : ISorter<T>
{
public void Sort(T[] array, IComparer<T> comparer) {//Sort}
}
internal class IntComparer : IComparer<int>
{
public int Compare(int x, int y)
{
return x.CompareTo(y);
}
}
internal static class RandomHelper
{
public static (int[] testArray, int[] correctArray) GetArrays(int n)
{
int[] testArr = new int[n];
int[] correctArr = new int[n];
Random rnd = new Random();
for (int i = 0; i < n; i++)
{
int t = rnd.Next(1, 1000);
testArr[i] = t;
correctArr[i] = t;
}
return (testArr, correctArr);
}
}
What I want to achieve that I want to do this with one test method:
static void Main(string[] args)
{
test<int>(new BubbleSorter<int>(), new IntComparer());
test<int>(new SelectionSorter<int>(), new IntComparer());
test<int>(new MergeSorter<int>(), new IntComparer());
}

test is supposed to be able to work on any T, right? But RandomHelper.GetArrays can only return int[]! To make your test method able to test any T, you need to write a GetArrays method that can generate test cases for any T as well, not just for int.
So now you have two choices:
Make test non-generic because you only need to sort ints here.
Make GetArrays work on any T.
For option 1, you can just remove all the <T> and replace T with int, like this:
class Program
{
static void Main(string[] args)
{
test(new BubbleSorter(), new IntComparer());
}
static void test(ISorter sorter, IComparer<int> intComparer)
{
var (correctArray, testArray) = RandomHelper.GetArrays(10);
sorter.Sort(testArray, intComparer);
// Array.Sort(correctArray); // you should do this in GetArrays instead! You should also pass intComparer to Array.Sort as well!
Console.WriteLine(string.Join(",", testArray));
Console.WriteLine(string.Join(",", correctArray));
}
}
public interface ISorter
{
void Sort(int[] array, IComparer<int> comparer);
}
class BubbleSorter : ISorter
{
public void Sort(int[] array, IComparer<int> comparer) {//Sort}
}
One way to do option 2 is to add a Func<int, T> parameter to GetArrays telling it how to transform an int to a T. You would also need a IComparer<T> parameter to sort the correct array as I mentioned above,
public static (T[] testArray, T[] correctArray) GetArrays<T>(int n, Func<int, T> transform, IComparer<T> comparer)
{
T[] testArr = new T[n];
T[] correctArr = new T[n];
Random rnd = new Random();
for (int i = 0; i < n; i++)
{
int t = rnd.Next(1, 1000);
testArr[i] = transform(t);
correctArr[i] = transform(t);
}
Array.Sort(correctArray, comparer);
return (testArr, correctArr);
}
And you can call GetArrays like this:
static void test<T>(ISorter<T> sorter, IComparer<T> intComparer)
{
var (correctArray, testArray) = RandomHelper.GetArrays(10, x => x, intComparer);
sorter.Sort(testArray, intComparer);
Console.WriteLine(string.Join(",", testArray));
Console.WriteLine(string.Join(",", correctArray));
}

Related

How to get array of fixed length from linq?

How can I get array of fixed length from Linq?
I have tried this and it doesn't work:
void Main() {
// To Array doesn't accept int argument
// Enumerable.Range(1, 3).ToArray(10);
Enumerable.Range(1, 3).Take(10);
// Result is [1,2,3], I expect [1,2,3,null,null, ..., null] or [1,2,3,0,0, ..., 0]
}
You can define your own extension method to convert an IEnumerable<T> to a fixed-length T[] like this:
static class EnumerableExtensions
{
public static T[] ToFixedLength<T>(this IEnumerable<T> source, int length)
{
var result = new T[length];
var i = 0;
foreach (var e in source)
{
if (i < length)
{
result[i++] = e;
}
}
return result;
}
}
Usage:
Enumerable.Range(1, 3).ToFixedLength(10)
You can do something like this.
class Test
{
static void Main()
{
int[] data = Enumerable.Range(1, 10).Select(x => x > 3 ? 0 : x).ToArray();
foreach (var x in data)
Console.WriteLine(x);
}
}

All combination of a list of tuples

I'm practicing some optimization problems and I'm stuck.
I have a list of tuples and I am doing the following:
private static int CalculateMinimumTotalCost(List<Tuple<int, int>> tuples)
{
int minimumCost = 0;
for(int i=0;i<tuples.Count()-1;i++)
{
minimumCost += Math.Max(Math.Abs(tuples[i].Item1 - tuples[i + 1].Item1), Math.Abs(tuples[i].Item2 - tuples[i + 1].Item2));
}
return minimumCost;
}
The idea is that given a list of tuples and this mathematical equation, I need to find the minimum cost. The catch is that the order of the tuples can be rearranged. My job is to find the LEAST costly arrangement of tuples.
So what I would like to do is loop through all possible combination of Tuples and return the combination with the minimum cost.
For example:
(1,2)(1,1)(1,3) = 3
(1,1)(1,2)(1,3) = 2
So in this case, i would return 2 because that arrangement is less costly.
I understand that when there are N tuples, the number of combinations is N!.
How do I get all the combinations possible for a list of tuples?
Thanks!
As other have suggested you should create the Point class:
public partial class Point
{
public int X { get; set; }
public int Y { get; set; }
public Point(int x, int y)
{
this.X = x;
this.Y = y;
}
}
And, let's encapsulate the functions for computing distance and total cost :
public partial class Point
{
public static int CalculateDistance(Point p0, Point p1)
{
return Math.Max(
Math.Abs(p0.X - p1.X),
Math.Abs(p0.Y - p1.Y)
);
}
}
public static class PointExtensions
{
public static int GetTotalCost(this IEnumerable<Point> source)
{
return source
.Zip(source.Skip(1), Point.CalculateDistance)
.Sum();
}
}
Finally, you will need another extension method to create "all possible combination" :
public static class PermutationExtensions
{
public static IEnumerable<IEnumerable<T>> GetPermutations<T>(this IEnumerable<T> source)
{
if (source == null || !source.Any())
throw new ArgumentNullException("source");
var array = source.ToArray();
return Permute(array, 0, array.Length - 1);
}
private static IEnumerable<IEnumerable<T>> Permute<T>(T[] array, int i, int n)
{
if (i == n)
yield return array.ToArray();
else
{
for (int j = i; j <= n; j++)
{
array.Swap(i, j);
foreach (var permutation in Permute(array, i + 1, n))
yield return permutation.ToArray();
array.Swap(i, j); //backtrack
}
}
}
private static void Swap<T>(this T[] array, int i, int j)
{
T temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
Source from Listing all permutations of a string/integer adapted to be more LINQ-friendly
Usage :
void Main()
{
var list = new List<Point>
{
new Point(1, 2),
new Point(1, 1),
new Point(1, 3),
};
// result: Point[] (3 items) : (1, 1), (1, 2), (1,3)
list.GetPermutations()
.OrderBy(x => x.GetTotalCost())
.First();
}
EDIT : As #EricLippert pointed out, source.OrderBy(selector).First() has some extra cost. This following extension method deals with this issue :
public static class EnumerableExtensions
{
public static T MinBy<T, TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector, IComparer<TKey> comparer = null)
{
IEnumerator<T> etor = null;
if (source == null || !(etor = source.GetEnumerator()).MoveNext())
throw new ArgumentNullException("source");
if (keySelector == null)
throw new ArgumentNullException("keySelector");
var min = etor.Current;
var minKey = keySelector(min);
comparer = comparer ?? Comparer<TKey>.Default;
while (etor.MoveNext())
{
var key = keySelector(etor.Current);
if (comparer.Compare(key, minKey) < 0)
{
min = etor.Current;
minKey = key;
}
}
return min;
}
}
And, we can rewrite the above solution as :
list.GetPermutations().MinBy(x => x.GetTotalCost())
You can change the for loop to Foreach to make it more readable and rather than using index to fetch values.
private static int CalculateMinimumTotalCost(List<Tuple<int, int>> tuples)
{
int minimumCost = 0;
Tuple<int, int> currentTuple = tuples.First();
foreach (Tuple<int, int> tuple in tuples)
{
minimumCost += Math.Max(Math.Abs(currentTuple.Item1 - tuple.Item1), Math.Abs(currentTuple.Item2 - tuple.Item2));
currentTuple = tuple;
}
return minimumCost;
}

Swapping two elements in a list covariantly

Can you write a swap routine for generic lists covariantly? Here is a swap routine which won't work:
public static void Swap(List<IComparable> list, int pos1, int pos2)
{
IComparable temp = list[pos1];
list[pos1] = list[pos2];
list[pos2] = temp;
}
Calling Swap(new List<int>{1,2}, 0, 1) won't work here because this version of Swap isn't covariant.
Does this work for you?
public static void Swap<T>(this List<T> list, int pos1, int pos2)
{
T tmp = list[pos1];
list[pos1] = list[pos2];
list[pos2] = tmp;
}
This allows you to specify the type and make the swap possible.

Creating array from existing one

This is the code:
class Program
{
void Add2Array(object arr, object item)
{
if (arr.GetType() == typeof(string[]))
{
int iLen = (arr as Array).Length;
var c = Array.CreateInstance(typeof (String), 3);
Array v = Array.CreateInstance((arr as Array).GetValue(0).GetType(), iLen+1); // this works but when if arr is empty it wont work
Array.Copy(ar, v, iLen);
v.SetValue(item, iLen);
}
}
public string[] sarr = new string[1];
static void Main(string[] args)
{
Program p = new Program();
p.sarr[0] = "String Item";
p.Add2Array(p.sarr, "New string item");
}
}
I want to create a method which can take every type of arrays and put new item into them.
Above code is my solution (if you know better please share) and if arr parameter hasn't any item, it won't work properly. Because if I use this Array.CreateInstance(arr.GetType(),3) it will create new array like this v.GetType() => string[2][] because arr is string array and if i create with same type it is returning two dimensonal array.
How can I extend an array(given as a parameter) and put new item into it ?
T[] Add2Array<T>(T[] arr, T item)
{
return arr.Concat(new[]{item}).ToArray();
}
Array cannot be extended. The only thing you could do is to copy data to a new array which is bigger than original one and append data.
BTW, why not to use List<>?
using System;
namespace ConsoleApplication1 {
class Program {
static void Main(string[] args) {
int[] x = new int[]{2,3,5};
int[] y = new ArrayExpander().AddItem(x, 0);
foreach (var i in y)
{
Console.Write(i);
}
}
}
class ArrayExpander
{
public T[] AddItem<T>(T[] source, T item)
{
var destination = new T[source.Length + 1];
Array.Copy(source, destination, source.Length);
destination[source.Length] = item;
return destination;
}
}
}

Multicast delegate always does the last operation

So I have the following the code:
namespace ConsoleApplication1
{
public delegate int Transformer(int x);
class Test
{
public static void Transform(int[] values, Transformer t)
{
for (int i = 0; i < values.Length; i++)
{
values[i] = t(values[i]);
}
}
static int Square(int x)
{
return x * x;
}
static int Minus(int x)
{
return x - 1;
}
static void Main()
{
int[] values = { 1, 2, 3 };
Transformer t = Test.Minus;
t += Test.Square;
Test.Transform(values, t);
foreach (int i in values)
{
Console.Write(i + " ");
}
}
}
}
Why is it always does only the last operation to the array(Square in my case). What should I need to change so it will do both Minus and Square ?
Multicast delegates always return the value of the last delegate in chain. Since you don't modify values in Test.Minus and Test.Square, but return new values, only latter is applied. The simplest way to fix this would to make your transformers take values by reference and modify them. e.g:
namespace ConsoleApplication1
{
public delegate void Transformer(ref int x);
class Test
{
public static void Transform(int[] values, Transformer t)
{
for (int i = 0; i < values.Length; i++)
{
t(ref values[i]);
}
}
static void Square(ref int x)
{
x = x * x;
}
static void Minus(ref int x)
{
x = x - 1;
}
static void Main()
{
int[] values = { 1, 2, 3 };
Transformer t = Test.Minus;
t += Test.Square;
Test.Transform(values, t);
foreach (int i in values)
{
Console.Write(i + " ");
}
}
}
}
Because the result is not chained through all the delegates
the code becomes the equivalent of
Minus(1);
return Square(1);
change the code to alter the variable in place.
public delegate void Transformer(ref int x);
public static void Transform(int[] values, Transformer t)
{
for (int i = 0; i < values.Length; i++)
{
t(ref values[i]);
}
}
static void Square(ref int x)
{
x*= x;
}
static void Minus(ref int x)
{
x--;
}
A far better solution would be to use a linq agregate because you could transform the solution without affecting the source.
public static int[] Transform(int[] values, params Func<int,int>[] t){
return values.Select(v=>t.Aggregate(v,(x,f)=>f(x))).ToArray();
}
Then you can just call
values=Transform(values,new[] { Minus,Square });
or
int[] values = {1,2,3};
int[] result = Transform(values,Minus,Square);
After this call values!=result so source is unchanged

Categories