How to add conditional inside Linq Aggregate in C#? - c#

I'm trying Linq over Imperative style, but I can't convert this conditional inside Aggregate to Linq.
Consider two following examples.
Simple example:
public enum Color {
Red, // Red score = 10
Yellow, // Yellow score = 5
Green, // Green score = 2
}
//Populate our sample list
public List<Color> Colors = new List<Color> {Red, Green, Green, Yellow};
//I need help on this one
public float Score => Colors.Aggregate(0.0f, (total, next) =>
{
//How to properly use conditional inside Aggregate?
if (next == Color.Red) {
return total + 10.0f;
} else if (next == Color.Yellow) {
return total + 5.0f;
} else if (next == Color.Green) {
return total + 2.0f;
}
//edit: forgot the default
return total;
}
Log(Score); //19
Edit: I have tried moving the conditional to Select, but then it will just move the problem, Which is how to add conditional inside Linq Select?
public float Score => Colors.Select(x =>
{
// The problem still happening
if (x == Color.Red) {
return 10.0f;
} else if (x == Color.Yellow) {
return 5.0f;
} else if (x == Color.Green) {
return 2.0f;
}
return 0.0f;
}
.Aggregate(0.0f, (total, next) => total + next);
And here is the complex example, basically it's just a stat modifier for a game,
// This is a Game Status Modifier, for example: "Strength 30 + 10%"
public enum StatModType
{
Flat = 100, // Flat addition to Stat
PercentAdd = 200, // Percent addition to Stat
... // many other type of addition
}
private float _baseValue = 30.0f;
public List<StatModifier> StatModifiers = new List<StatModifier>
{...} //dummy data
public float Value => StatModifiers.Aggregate(_baseValue, (finalValue, mod) =>
{
//I need help on this one
if (mod.Type == StatModType.Flat)
return finalValue + mod.Value;
else if (mod.Type == StatModType.PercentAdd)
// When we encounter a "PercentAdd" modifier
return finalValue + finalValue * mod.Value;
else if (mod.Type == ...)
//and continues below everytime I add more modifier types..
}
Log(Value); // Strength = 33;
Edit: I'll just post (Credit: https://forum.unity.com/threads/tutorial-character-stats-aka-attributes-system.504095/) the imperative code in case someone needs it, I also have a hard time reading this one:
private float CalculateFinalValue()
{
float finalValue = BaseValue;
float sumPercentAdd = 0; // This will hold the sum of our "PercentAdd" modifiers
for (int i = 0; i < statModifiers.Count; i++)
{
StatModifier mod = statModifiers[i];
if (mod.Type == StatModType.Flat)
{
finalValue += mod.Value;
}
else if (mod.Type == StatModType.PercentAdd) // When we encounter a "PercentAdd" modifier
{
sumPercentAdd += mod.Value; // Start adding together all modifiers of this type
// If we're at the end of the list OR the next modifer isn't of this type
if (i + 1 >= statModifiers.Count || statModifiers[i + 1].Type != StatModType.PercentAdd)
{
finalValue *= 1 + sumPercentAdd; // Multiply the sum with the "finalValue", like we do for "PercentMult" modifiers
sumPercentAdd = 0; // Reset the sum back to 0
}
}
else if (mod.Type == StatModType.PercentMult) // Percent renamed to PercentMult
{
finalValue *= 1 + mod.Value;
}
}
return (float)Math.Round(finalValue, 4);
}
How can I add conditional inside Aggregate / Reduce / Scan function?

I suggest extracting model in both cases i.e.
Simple Example:
private static Dictionary<Color, float> s_ColorScores = new Dictionary<Color, float>() {
{Color.Red, 10.0f},
{Color.Yellow, 5.0f},
{Color.Green, 2.0f},
};
...
float Score = Colors
.Sum(color => s_ColorScores[color]);
Complex Example:
private static Dictionary<StatModType, Func<float, float, float>> s_Modifications = new
Dictionary<StatModType, Func<float, float, float>> {
{StatModType.Flat, (prior, value) => prior + value},
{StatModType.PercentAdd, (prior, value) => prior + prior * value},
//TODO: add modification rules here
};
public float Value => StatModifiers
.Aggregate(_baseValue, (prior, mod) => s_Modifications[mod.Type](prior, mod.Value));
So you are going to have game's model (s_ColorScores, s_Modifications...) with rules, settings, balances etc. (which you will probably want to tune, may be Color.Yellow score of 6.0f is a better choice) separated from simple business logics.

Assuming that the behaviors associated to the enum types are static and not dynamic, based on this MSDocs article another approach would be to use enumeration classes instead of enum types. To simplify this, you could use the SmartEnum package.
Using this lib and approach, your use cases turn into:
Simple Example:
public sealed class Color: SmartEnum<Color>
{
public static readonly Color Red = new Color (nameof(Red), 1, 10.0f);
public static readonly Color Yellow = new Color (nameof(Yellow), 2, 20.0f);
public static readonly Color Green = new Color (nameof(Green), 3, 30.0f);
private Color(string name, int value, double score)
: base(name, value)
{
this.Score = score;
}
public float Score {get;}
}
float TotalScore = Colors
.Sum(color => color.Score);
Complex Example:
public sealed class StatMod: SmartEnum<StatMod>
{
public static readonly StatMod FlatAdd = new StatMod(nameof(FlatAdd), 200, (prev, val)=>prev+val);
public static readonly StatMod PercentAdd = new StatMod(nameof(PercentAdd), 300, (prev,val)=>prior + prior * value);
private StatMod(string name, int value, Func<float, float, float> reduce) : base(name, value)
{
this.Reduce = reduce;
}
public Func<float, float, float> Reduce {get;}
}
public float Value => StatModifiers
.Aggregate(_baseValue, (prior, mod) => mod.Reduce(prev, mod.Value));

Related

Sine predictions from ML.NET not really working

I am currently experimenting with ML.NET but with the very first project I get stuck. I am trying to make a prediction of sine values.
Generating a list of X and Y values with a function for sine (y = sin(x))
Using that list for ML.NET to learn
Make Y-predictions for the next X-values
Append these predictions to the list
Result: I am always getting one single result for any following number.
Sine is just as a varifyable function.
This is the current code:
class Program
{
private const string FILEPATH = #"sinus.txt";
private const float XSTART = 0f;
private const float XEND = 20f;
private const float XSTEP = 0.1f;
private const float XEND_FORECAST = 30f;
static void Main(string[] args)
{
GenerateSinusList();
var pipeline = new LearningPipeline();
pipeline.Add(new TextLoader(FILEPATH).CreateFrom<Sinus>(separator: ';'));
pipeline.Add(new ColumnConcatenator("Features", "X"));
pipeline.Add(new FastTreeRegressor());
var model = pipeline.Train<Sinus, SinusForecast>();
PredictUpcomingValues(model);
Console.WriteLine("done");
Console.ReadLine();
}
static void PredictUpcomingValues(PredictionModel<Sinus, SinusForecast> model)
{
using (var sw = System.IO.File.AppendText(FILEPATH))
{
sw.WriteLine();
for (double i = XEND + XSTEP; i < XEND_FORECAST; i += XSTEP)
{
var prediction = model.Predict(new Sinus() { X = (float)i });
var t = string.Format("{0};{1}", i, prediction.ResultY);
sw.WriteLine(t.Replace(',', '.')); //Quick localization fixSine
}
sw.Close();
}
}
static void GenerateSinusList()
{
var sinus = GenerateSine(XSTART, XEND, XSTEP);
var text = string.Join(System.Environment.NewLine, sinus.Select(x => string.Format("{0:};{1}", x.Key, x.Value)));
System.IO.File.WriteAllText(FILEPATH, text.Replace(',', '.'));
}
static Dictionary<float, float> GenerateSine(float from, float to, float step)
{
Dictionary<float, float> result = new Dictionary<float, float>((int)((to - from) / step) + 1);
for (double i = from; i < to; i += step)
{
result[(float)i] = (float)Math.Sin(i);
}
return result;
}
public class Sinus
{
[Column("0")]
public float X;
[Column("1", name: "Label")]
public float Y;
}
public class SinusForecast
{
[ColumnName("Score")]
public float ResultY;
}
}
The result of this: Each value > 20 returns 0.5429355. The list looks like that:
...
19.4;0.523066
19.5;0.6055401
19.6;0.6819639
19.7;0.7515736
19.8;0.8136739
19.9;0.8676443
20.1;0.5429355 << first predicted
20.2;0.5429355
20.3;0.5429355
20.4;0.5429355
20.5;0.5429355
20.6;0.5429355
...
Edit: I am Using ML 0.4.0
Decision trees are not very good at extrapolation (i.e. predicting on data that is outside the range of the training data). If you make predictions on the training data, the scores will not be constant and will actually be somewhat reasonable.
One approach to turn this into an interpolation problem is to map all the inputs to their corresponding values in one period of the sine function. If you add another features column that is mod(X, 2*Pi), you get great predictions on the test data as well.

Finding Coordinates of Line-Line Intersection in C#

to start with I made a simple code that checks if 2 lines collide based on 4 points that are given by their x and y coordinates. It checks if the angle (variable k in my code) of both lines is the same, in that case they are parallel, otherwise they collide. The angle (k) was calculated based on the math equasion Click here [k = (y2-y1)/(x2-x1)]. Now I am not sure how to get the point they are colliding in. I would appreciate it very much if you could help me. Thank you in advance.
My Code: (method I call to calculate angle)
static void MetodaTrazenjaPresjeka(Duzina d1, Duzina d2)
{
int k11 = d1.Krajy - d1.Pocy; //y2-y1 - first line
int k12 = d1.Krajx - d1.Pocx; //x2-x1 - first line
double k1 = (double)k11 / k12; //angle of the first line
int k21 = d2.Krajy - d2.Pocy; //y2-y1 - second line
int k22 = d2.Krajx - d2.Pocx; //x2-x1 - second line
double k2 = (double)k21 / k22; //angle of the second line
if (k1 == k2)
{
//they are parallel
Console.WriteLine("MOJA METODA:");
Console.WriteLine("-----------------------------------");
Console.Write("Pravci zadani tockama su paralelni!");
}
else
{
//lines are colliding
Console.WriteLine("MOJA METODA:");
Console.WriteLine("-----------------------------------");
Console.Write("Pravci zadani tockama se sijeku!");
}
}
Code in class Duzina:
class Duzina
{
private int pocx, pocy, krajx, krajy;
//read/write attribute for the x coordinate of the first point
public int Pocx
{
get { return pocx; }
set { pocx = value; }
}
//read/write attribute for the y coordinate of the first point
public int Pocy
{
get { return pocy; }
set { pocy = value; }
}
//read/write attribute for the x coordinate of the second point
public int Krajx
{
get { return krajx; }
set { krajx = value; }
}
//read/write attribute for the y coordinate of the second point
public int Krajy
{
get { return krajy; }
set { krajy = value; }
}
//method that will print out coordinates of the given points
public void Ispis()
{
Console.Write("Pocetna tocka: ({0},{1})",Pocx,Pocy);
Console.Write("Krajnja tocka: ({0},{1})", Krajx, Krajy);
}
}
line equation: y = m * x + b
1) m_d1 = (d1.y2 - d1.y1) / (d1.x2 - d1.x1)
b_d1 = d1.y1 - m_d1 * d1.x1
(same for m_d2 and b_d2)
2) intersect.y = m_d1 * intersect.x + b_d1
intersect.y = m_d2 * intersect.x + b_d2
3) m_d1 * intersect.x + b_d1 = m_d2 * intersect.x + b_d2
4) intersect.x = (b_d2 - b_d1) / (m_d1 - m_d2)
now plug intersect.x that was obtained from 4) back into either equation in 2) to get intersection.y

C# custom sort by distance from a point

I am trying to sort a list using IComparer there are about 20,000 items in my list. The first approximately 100 and last 100 items are sorted but the center is not. I am not sure what I am doing wrong. Attached is the parts of the code that does the sort.
class myclass:me
{
private void mysort()
{
// Sort the data
DistComparer dc = new DistComparer();
st.Sort(dc);
}
}
class DistComparer : IComparer<Tower>
{
public int Compare(st x, st y)
{
double dist1 = getDistance(new PointF(45.0f, -80f), new PointF(x.Lat, x.Long));
double dist2 = getDistance(new PointF(45.0f, -80f), new PointF(y.Lat, y.Long));
if (dist1 > dist2)
return 1;
else if (dist1 == dist2)
return 0;
else
return -1;
}
private static double getDistance(PointF pt1, PointF pt2)
{
return Math.Sqrt(Math.Pow((pt1.X - pt2.X), 2) + Math.Pow((pt1.Y - pt2.Y), 2));
}
}
}

List.Find in Unity environment seems to depend on type casting

The setup for my List and the item class are as such:
public class mapVertex {
public Vector2 vertex;
public float x {
get{ return vertex.x; }
}
public float z {
get{ return vertex.y; }
}
public mapVertex(float x, float y) {
myID = ID++;
vertex.x = x;
vertex.y = y;
}
}
void Start() {
...
vertexList = new List<mapVertex>();
for (...)
vertexList.Add( new mapVertex( (float) 10.1, (float) 11.2 ) );
Following immediately in the code is the Find statement. After execution of the below however newEdge.B is equal to null.
for (...)
mapEdge newEdge = new mapEdge();
double[] vve = new double[2];
vve[0] = 10.1;
vve[1] = 11.2;
newEdge.B = vertexList.Find( x => (x.x == (float) vve[0]) && (x.z == (float) vve[1]) );
...
}
The below evaluates to true in the expression evaluator in MonoDevelop when I breakpoint on the line in question and the debugger confirms the values look correct, so I'm not sure why List.Find() isn't returning the first element of vertexList.
(vertexList[0].x == (float) vve[0]) && (vertexList[0].z == (float) vve[1])
Is this possibly related to a type conversion issue? The values of the items in vertexList were originally created by casting double to float (see above), but from what I understand doubles should be deterministic when casting to float.
Edit:
When changing
double[] vve = new double[2];
vve[0] = 10.1;
vve[1] = 11.2;
to
float[] vve = new float[2];
vve[0] = 10.1f;
vve[1] = 11.2f;
this seems to work..
Anyone know why?

Concurrent Dictionary AddOrUpdate method 3rd parameter?

private readonly ConcurrentDictionary<string, System.Drawing.Color> _colorSet;
public void BuildColorSet(IList<string> colorNames, string prefix, bool forceLastToGray)
{
var size = forceLastToGray ? colorNames.Count - 1 : colorNames.Count;
int nbHue = 6;
int nbCycle = (int)Math.Ceiling((double)size / nbHue);
var saturationMax = nbCycle <= 2 ? 1.0 : 1.0;
var saturationMin = 0.3;
var luminanceMax = nbCycle <= 2 ? 0.85 : 0.85;
var luminanceMin = 0.3;
var maxSaturationShift = 0.30;
var maxLuminanceShift = 0.15;
var interval = 1.0 / Math.Min(size, nbHue);
var saturationShift = (saturationMax - saturationMin) / (nbCycle - 1);
saturationShift = Math.Min(saturationShift, maxSaturationShift);
var luminanceShift = (luminanceMax - luminanceMin) / (nbCycle - 1);
luminanceShift = Math.Min(luminanceShift, maxLuminanceShift);
var hueShift = 0.0;
var saturation = saturationMax;
var luminance = luminanceMax;
for(var i = 0; i<size; i++)
{
if(i > 0 && (i % nbHue == 0)) // Next Cycle
{
saturation -= saturationShift;
luminance -= luminanceShift;
hueShift = hueShift == 0 ? interval/2 : 0;
}
var hue = interval*(i%nbHue) + hueShift;
System.Drawing.Color color = HSL2RGB(hue, saturation, luminance);
_colorSet.AddOrUpdate(prefix + colorNames[i], color, ???);
}
if(forceLastToGray)
{
_colorSet.TryAdd(prefix + colorNames[colorNames.Count - 1], System.Drawing.Color.LightGray);
}
_cssDirty = true;
}
I want to be able to update the dictionary if the color exists with new value. And also add to dictionary if the color is not there in dictionary.
I am using the AddOrUpdate but not able to get the 3rd parameter(form the lambda expression OR delegate method) of the AddOrUpdate method.
Any idea how my 3rd parameter would look like?
From the documentation:
updateValueFactory
Type: System.Func
The function used to generate a new value for an existing key based on the key's existing value
This will leave the value in the collection alone if it already exists:
_colorSet.AddOrUpdate(prefix + colorNames[i], color,
(key, existingVal) =>
{
return existingVal;
});
This will replace the value in the collection with the same one specified for the insert:
_colorSet.AddOrUpdate(prefix + colorNames[i], color,
(key, existingVal) =>
{
return color;
});
You can perform conditional logic, comparisons between the old value and new value, or update the original object in the function, for example.
_colorSet.AddOrUpdate(prefix + colorNames[i], color,
(key, existingVal) =>
{
if (existingVal.Name == "Red")
return existingVal;
else
return color;
});
As per the web page asawyer gave you, what's required is a function
Func<TKey, TValue, TValue>
In this case it looks like you are passing a string and a Color but how you want to combing them is largely upto you. You need a function that returns a Color so the following should work from a syntax perspective.
(key, oldValue) => oldValue
I've no idea who you might calculating the new value. You could for example use your new color
_colorSet.AddOrUpdate(prefix + colorNames[i], color, (key, oldValue) => color);
It looks like you don't care if the color is already there; you always want to update the value in the dictionary. In that case you're better off using the normal indexer, e.g.
_colorSet[prefix + colorNames[i]] = color;

Categories

Resources