C# 如何处理C语言中的大量变量#
我在做一个关于遗传学的游戏,我偶然遇到了一个小问题。(事实上这更像是一个问题) 我有一个DNA类,里面有很多变量,用来存储游戏中每个“生物”的信息C# 如何处理C语言中的大量变量#,c#,variables,C#,Variables,我在做一个关于遗传学的游戏,我偶然遇到了一个小问题。(事实上这更像是一个问题) 我有一个DNA类,里面有很多变量,用来存储游戏中每个“生物”的信息 public class DNA { float size; float speed; float drag; float eyeSize; int numberOfEyes; float color_r; [...] } 现在让我们想象一下,我想平均两个DNA 我可以做到: DNA Aver
public class DNA {
float size;
float speed;
float drag;
float eyeSize;
int numberOfEyes;
float color_r;
[...]
}
现在让我们想象一下,我想平均两个DNA
我可以做到:
DNA AverageDNAs (DNA dna1, DNA dna2) {
DNA newDNA = new DNA ();
newDNA.size = (dna1.size+dna2.size)/2f;
newDNA.speed = (dna1.speed+dna2.speed)/2f;
[...]
}
但它看起来很长,每次我要做一些计算,我需要一个一个地重写每个变量
因此,我创建了一个函数,将所有变量(在0和1之间标准化)存储到一个列表中
public class DNA {
public float size;
public float speed;
[...]
private List<float> tempList;
public List<float> ToList() {
if (tempList == null) {
tempList = new List<float>();
toReturn.Add (size/sizemax);
toReturn.Add (speed/speedmax);
[...]
}
return tempList
}
public static ADN fromList(List<float> list) {
ADN toReturn = new ADN();
toReturn.size = list[0]*sizemax;
[...]
}
}
公共类DNA{
公众浮点数;
公众浮标速度;
[...]
私人名单圣殿骑士;
公众名单收费表(){
if(templast==null){
tempList=新列表();
t返回。添加(尺寸/尺寸最大值);
t返回。添加(速度/速度最大值);
[...]
}
回归圣堂武士
}
公共静态ADN fromList(列表){
ADN toReturn=新ADN();
toReturn.size=list[0]*sizemax;
[...]
}
}
现在,我可以做:
DNA AverageDNAs (DNA dna1, DNA dna2) {
List<float> dnaList1 = dna1.ToList();
List<float> dnaList2 = dna2.ToList();
List<float> newDNA = new List<float>();
for (int i = 0; i<dnaList1.Count; i++) {
newDNA.Add ((dnaList1[i]+dnaList2[i])/2f);
}
return DNA.fromList(newDNA);
}
DNA平均DNA(DNA dna1,DNA dna2){
List dnaList1=dna1.ToList();
List dnaList2=dna2.ToList();
List newDNA=newlist();
对于(int i=0;i如果始终对所有应用相同的操作,则可以使用参数指定要执行的操作:
public DNA Cross(DNA dna1, DNA dna2, Func<float, float, float> operation)
{
var newDNA = new DNA();
newDNA.size = operation(dna1.size, dna2.size);
newDNA.speed = operation(dna1.speed, dna1.speed);
// And all the rest
}
等等
如果您确实希望更有选择性地应用该操作,还可以扩展交叉
方法,以获取一个枚举,指定要更新的字段。当然,您可能还希望添加另一个Func
(或操作
,具体取决于您对变异参数的适应程度)以任何您想要的方式更新其余部分。我认为更好的方法是在DNA类上定义您自己的算术运算符。有关如何操作,请参阅此
public class DNA {
float size;
float speed;
float drag;
float eyeSize;
int numberOfEyes;
float color_r;
[...]
public static DNA operator +(DNA dna1, dna1 rhs)
{
DNA newDNA = new DNA ();
newDNA.size = (dna1.size+dna2.size);
newDNA.speed = (dna1.speed+dna2.speed);
[...]
return newDNA;
}
}
然后你可以做:
var newDna = dna1 + dna2;
我想你可以在内部使用@Luaan suggestion来避免一遍又一遍地重复输入同一个东西,但是使用IMHO的时候会更整洁
public static DNA operator +(DNA dna1, dna1 rhs)
{
return Cross(dna1, dna2, (d1, d2) => d1 + d2);
}
您可以使用反射来获取属性,然后在对象列表中循环使用它们吗
如果您不熟悉反射,下面是一个MSDN示例,应该会有所帮助:
使用字典而不是字段。因此,当您需要获得平均值时,可以使用:
foreach(var item in this._dict)
{
avgDNA._dict[item.Key] = (item.Value + other._dict[item.Key]) / 2;
}
这样,您就不必关心新字段。您可以围绕一个特征定义一个接口/类模型,它知道如何与一个类似的特征“组合”。例如,一个表示为float的trair,其中组合是一个平均值,可以表示为:
public class FloatTrait
{
private float val;
public FloatTrait(float val)
{
this.val = val;
}
public float Value{get { return this.val; }}
public FloatTrait CombineWith(FloatTrait t)
{
return new FloatTrait((this.Value + t.Value)/2.0f);
}
}
然后,您可以在DNA
对象中拥有表示特征的属性,而不是绝对值
public class DNA
{
public FloatTrait Size{get;set;}
public FloatTrait Speed{get;set;}
}
添加了一个字典来引用它们,这使得它们的组合更加简单(值得注意的是:我使用字符串文字作为键,使用枚举更合适!h/t@Luaan)
公共类DNA
{
私有字典特性=新字典(){
{“大小”,新的FloatTrait(5.0)},
{“速度”,新的FloatTrait(50.0)},
}
公共DNA(字典目录)
{
this.traits=dict;
}
public FloatTrait this[string key]{get{return traits[key];}
公共浮点大小{get{return traits[“Size”].Value;}
公共浮点速度{get{return traits[“Speed”].Value;}
公共DNA组合(其他DNA)
{
var newDict=this.traits.ToDictionary(k=>k.Key
v=>v.Value.CombineWith(其他[v.Key]);
返回新的DNA(newDict);
}
}
您可以扩展它以支持除由浮点表示的那些之外的“特征”。如果平均值不合适,您还可以更改“组合”特征的机制
这个工作的实例:我在这里假设您并不总是执行相同的操作,并且操作取决于对象的类型
首先要做的是为定义基本功能的float
添加一个额外的抽象层
public interface IDNAComputable
{
IDNAComputable Grow(float length);
// etc.
}
如果最终大多数方法都是“空”的,那么将其作为一个抽象类,并定义一些不起任何作用的基本实现
接下来,添加一个封装float、int、string或任何您喜欢的类。您可能希望像我一样创建语义类,如weight、height等。在本例中,我使用struct,但如果您在其中填充了大量内容,您可能希望使用类。这里的主要问题是,它们都共享一个我们可以用来处理内容的接口:
public struct Height : IDNAComputable
{
// implement members
}
更新后,您似乎还想剪切内容等。可能需要添加一个额外的枚举来描述属性。因为它们映射到整数,所以您可以使用它们做很多有趣的事情,包括随机索引等
public enum DNAProperty : int
{
BodyWidth,
BodyHeight,
MouthWidth,
MouthHeight,
// ...
}
最后一件事是构建DNA本身:
public class DNA
{
private Width bodyWidth;
private Height bodyHeight;
private Width mouthWidth;
private Height mouthHeight;
// etc.
public void Update(Func<IDNAComputable, IDNAComputable, DNAProperty> op)
{
bodyHeight = (Height)op(bodyHeight, DNAProperty.BodyHeight);
bodyWidth = (Width)op(bodyWidth, DNAProperty.BodyWidth);
// etc for all other stuff.
}
public void Merge(Func<IDNAComputable, IDNAComputable, IDNAComputable, DNAProperty> op, DNA otherCreature)
{
bodyHeight = (Height)op(bodyHeight, otherCreature.bodyHeight, DNAProperty.BodyHeight);
bodyWidth = (Width)op(bodyWidth, otherCreature.bodyWidth, DNAProperty.BodyWidth);
// etc for all other stuff.
}
}
根据您的评论更新一些实际示例:
现在我正在使用ToList()方法来比较两个DNA(以了解它们之间的差异),对一个随机变量应用一个突变,并交叉两个DNA(我不是平均值,这只是一个示例)
使用这种方法计算差异很容易,即使您的“高度”属性与“宽度”属性不同。如果愿意,您甚至可以引入“身体质量”属性,等等:
double diff = 0;
creature.Merge((lhs, rhs, prop) => { diff += lhs.Difference(rhs); return lhs; });
…或者,如果您只想为此使用一组特定的属性,您也可以这样做:
double diff = 0;
creature.Merge((lhs, rhs, prop) =>
{
if (prop == DNAProperty.BodyMass)
{
diff += lhs.Difference(rhs);
}
return lhs; });
列表非常有用,因为我只需要选择交叉点、迭代列表和“剪切”
不需要列表:
double diff = 0;
creature.Merge((lhs, rhs, prop) =>
{
diff += lhs.Difference(rhs);
if (diff > SomeCutThreshold) { return lhs; }
else { return rhs; }
});
…或者你想知道切入点在哪里
切割点的意思是:for(inti=0;icutPoint?dna1[i]:dna2[i]}
总结
虽然这看起来可能需要更多的工作,但如果你做出了正确的抽象和继承树,你应该得到更少的代码,而不是更多
double diff = 0;
creature.Merge((lhs, rhs, prop) => { diff += lhs.Difference(rhs); return lhs; });
double diff = 0;
creature.Merge((lhs, rhs, prop) =>
{
if (prop == DNAProperty.BodyMass)
{
diff += lhs.Difference(rhs);
}
return lhs; });
double diff = 0;
creature.Merge((lhs, rhs, prop) =>
{
diff += lhs.Difference(rhs);
if (diff > SomeCutThreshold) { return lhs; }
else { return rhs; }
});
double diff = 0;
DNAProperty cutpoint = (DNAProperty)int.MaxValue;
creature.Merge((lhs, rhs, prop) =>
{
diff += lhs.Difference(rhs);
if (diff > SomeCutThreshold) { cutpoint = prop; diff = double.MinValue; }
return lhs;
});
creature.Merge((lhs, rhs, prop) => ((int)prop > (int)cutpoint)?lhs:rhs);