【问题标题】:C# examining and replacing tuple values based on other tupleC# 根据其他元组检查和替换元组值
【发布时间】:2020-10-07 11:52:02
【问题描述】:

我从编程和 C# 开始,我有两个元组。一个元组代表一个点列表:

static List<(string, string, string)> PR { get; set; } = new List<(string, string, string)>()
    {
        ("P1", "0", "0"),
        ("P2", "P1", "P1+Height"),
        ("P3", "P1+Width", "P2"),
        ("P4", "P3", "P3+Height")
    };

其中,元组列表中的 Item1 代表点名称(P1、P2、P3、P4),Item2 和 Item3 分别代表点的 x 值和 y 值的参数公式。

上述列表中第二项中的“P1”应查找以“P1”开头的元组,然后查找该元组中的第二项,在本例中为 0。

我有第二个元组列表,表示计算上述点值所需的参数。

static List<(string, double)> PAR { get; set; } = new List<(string, double)>()
    {
        ("Height", 500),
        ("Width", 1000)
    };

假设我要计算参数公式“P3+Height”的值如下:

P3+Height --> P2 (+Height) --> P1+Height (+Height) --> 0 (+Height (+Height) --> 0 + Height + Height;

最后我想用实际值(0 + 500 + 500 -> P3+Height = 1000)替换参数字符串,但那是以后关心的。

问题:我正在尝试创建一个函数,它可以递归地评估元组列表并保留参数名称,但也会查找相应的元组,直到我们到达结束或退出情况。这就是我现在所处的位置,但我很难在实际工作代码中得到我的思考过程:

        static void Main(string[] args)
    {
        //inputString = "P3+Height"
        string inputString = PR[3].Item3;

        string[] returnedString = GetParameterString(inputString);


        #region READLINE
        Console.ReadLine();
        #endregion
    }

    private static string[] GetParameterString(string inputString)
    {
        string[] stringToEvaluate = SplitInputString(inputString);

        for (int i = 0; i < stringToEvaluate.Length; i++)
        {
            //--EXIT CONDITION
            if (stringToEvaluate[0] == "P1")
            {
                stringToEvaluate[i] = "0";
            }

            else
            {
                if (i % 2 == 0)
                {
                    //Check if parameters[i] is point string
                    var value = PAR.Find(p => p.Item1.Equals(stringToEvaluate[i]));

                    //Check if parameters[i] is double string
                    if (double.TryParse(stringToEvaluate[i], out double result))
                    {
                        stringToEvaluate[i] = result.ToString();
                    }
                    else if (value == default)
                    {
                        //We have a point identifier
                        var relatingPR = PR.Find(val => val.Item1.Equals(stringToEvaluate[i])).Item2;
                        
                        //stringToEvaluate[i] = PR.Find(val => val.Item1.Equals(pointId)).Item2;
                        stringToEvaluate = SearchParameterString(relatingPR);
                    }
                    else
                    {
                        //We have a parameter identifier
                        stringToEvaluate[i] = value.Item2.ToString();
                    }
                }
            }
        }
        return stringToEvaluate;
    }

    private static string[] SplitInputString(string inputString)
    {
        string[] splittedString;
        splittedString = Regex.Split(inputString, Delimiters);
        return splittedString;
    }

谁能指出我如何通过递归或其他更好、更简单的方法来完成这项工作的正确方向?

最后,我需要得到一个这样的元组列表:

("P1", "0", "0"),
("P2", "0", "500"),
("P3", "1000", "500"),
("P4", "1000", "1000")

提前致谢!

【问题讨论】:

  • 嘿。您尝试执行此操作的方式确实很复杂。为什么不使用定义元组一个条目的结构或类?
  • 嗨,我认为您需要按照@AdrianEfford 的建议为代码添加更多结构,并且还需要使用链表或树来保存节点。这将简化问题。

标签: c# recursion tuples


【解决方案1】:

我写了一些东西来做到这一点 - 我改变了一些结构以简化代码和运行时,但它仍然返回你期望的元组:

    // first I used dictionaries so we can search for the corresponding value efftiantly: 
    static Dictionary<string, (string width, string height)> PR { get; set; } =
        new Dictionary<string, (string width, string height)>()
    {
            { "P1", ("0", "0") },
            { "P2", ("P1", "P1+Height")},
            { "P3", ("P1+Width", "P2") },
            { "P4", ("P3", "P3+Height") }
    };

    static Dictionary<string, double> PAR { get; set; } = new Dictionary<string, double>()
        {
            { "Height", 500 },
            { "Width", 1000 }
        };

    static void Main(string[] args)
    {
        // we want to "translate" each of the values height and width values
        List<(string, string, string)> res = new List<(string, string, string)>();
        foreach (var curr in PR)
        {
            // To keep the code generic we want the same code to run for height and width- 
            // but for functionality reasons we need to know which it is - so sent it as a parameter.
            res.Add((curr.Key,
                    GetParameterVal(curr.Value.width, false).ToString(),
                    GetParameterVal(curr.Value.height, true).ToString()));
        }

        #region READLINE
        Console.ReadLine();
        #endregion
    }


private static double GetParameterVal(string inputString, bool isHeight)
{
    // for now the only delimiter is + - adapt this and the code when \ as needed
    // this will split string with the delimiter ("+height", "-500", etc..)
    string[] delimiters = new string[] { "\\+", "\\-" };
    string[] stringToEvaluate = 
        Regex.Split(inputString, string.Format("(?=[{0}])", string.Join("", delimiters)));

    // now we want to sum up each "part" of the string
    var sum = stringToEvaluate.Select(x =>
    {
        double result;
        int factor = 1;
        // this will split from the delimiter, we will use it as a factor, 
        // ["+", "height"], ["-", "500"] etc..
        string[] splitFromDelimiter=
            Regex.Split(x, string.Format("(?<=[{0}])", string.Join("|", delimiters)));
        if (splitFromDelimiter.Length > 1) {
            factor = int.Parse(string.Format($"{splitFromDelimiter[0]}1"));
            x = splitFromDelimiter[1];
        }

        if (PR.ContainsKey(x))
        {
            // if we need to continue recursively translate
            result = GetParameterVal(isHeight ? PR[x].height : PR[x].width, isHeight);
        }
        else if (PAR.ContainsKey(x))
        {
            // exit condition
            result = PAR[x];
        }
        else
        {
            // just in case we didnt find something - we should get here
            result = 0;
        }

        return factor * result;
    }).Sum();

    return sum;
}

    }
}

我没有添加任何有效性检查,如果没有找到一个值,它会收到一个 0 的值,但请继续调整它以适应您的需求..

【讨论】:

  • 感谢您提供的帮助和示例。这个比较容易理解。我什至可以有随机参数字符串,对吗?不过,我还有一个问题:如果我还希望能够使用“-”作为运算符怎么办?那将如何适应?我也希望能够使用它。在每种情况下,我们都可以确定要评估的字符串始终以参数名称或对 PR 字典的点引用开头。
  • 我不确定你所说的随机参数字符串是什么意思,但是是的,PAR 和 PR 可以有尽可能多的值和你想要的任何值 :) 只要记住,如果没有找到它''将被视为“0” - 所以要小心你放在那里的东西(我们可能会抛出异常或写一条消息,我会把它留给你):)
  • 是的,我就是这个意思。这需要进入一个更大的程序,根据两个字典计算点坐标。这些点将用于创建配置文件实例。该配置文件用于另一个 3d 构建软件,该软件基于配置文件生成 3d 实体。我在控制台应用程序中对它进行了本地测试,一切正常。谢谢现在,我将继续在更大的整体中实施它。再次感谢你们!
  • 当然 :) 如果此答案解决了您的问题,请考虑通过单击复选标记接受它。这向更广泛的社区表明您已经找到了解决方案,并为回答者和您自己提供了一些声誉。当然,没有义务这样做。 meta.stackexchange.com/questions/5234/…
【解决方案2】:

这是您问题的一个工作示例...我花了很多时间,所以希望您能欣赏它:
整个代码逐行注释。如果您有任何问题,请随时问我!

首先,我们创建一个名为myEntry 的类,它将代表一个条目。名称必须是唯一的,例如 P1、P2、P3

public class myEntry
    {
        public string Name { get; private set; } //this field should be unique
        public object Height { get; set; } //Can contain a reference to another entry or a value also as many combinations of those as you want. 
                                            // They have to be separated with a +

        public object Width { get; set; } //same as for height here

        public myEntry(string name, object height, object width)
        {
            //Set values
            this.Name = name;
            this.Height = height;
            this.Width = width;
        }
    }

现在我为另一个类中的异常创建一个虚拟 Exception 类(您将在后面看到它的用法。暂时忽略它)

public class UnknownEntry : Exception
    {
        //Create a new Class that represents an exception
    }

现在我们创建一个重要的类来处理条目并为我们完成所有工作。这可能看起来很复杂,但如果您不想花时间理解它,您可以复制粘贴它,这是一个可行的解决方案!

public class EntryHolder
    {
        private Dictionary<string, double> _par = new Dictionary<string, double>(); //Dictionary holding our known variables

        private List<myEntry> _entries; //List holding our entries
        

        public EntryHolder()
        {
            _entries = new List<myEntry>(); //Create list

            //Populate dictionary
            _par.Add("Height", 500);
            _par.Add("Width", 1000);
        }

        public bool Add(myEntry entry)
        {
            var otherEntry = _entries.FirstOrDefault(x => x.Name.Equals(entry.Name)); //Get entry with same name
            if(otherEntry != null)
            {
                //Entry with the same name as another entry

                //throw new DuplicateNameException(); //Throw an exception if you want
                return false; //or just return false
            }

            //Entry to add is valid
            _entries.Add(entry); //Add entry
            return true; //return success
        }

        public void Add(List<myEntry> entries)
        {
            foreach (var entry in entries) //Loop through entries
            {
                Add(entry);
            }
        }

        public myEntry GetEntry(string uniqueName)
        {
            var entry = GetRawEntry(uniqueName); //Get raw entry

            var heightToCalculate = entry.Height.ToString(); //Height to calculate to string
            var widthToCalculate = entry.Width.ToString(); //Width to calculate to string

            entry.Height = Calculate(heightToCalculate, true); //Calculate height
            entry.Width = Calculate(widthToCalculate, false); //Calculate width
            return entry; //return entry
        }

        public List<myEntry> CalculateAllEntries()
        {
            List<myEntry> toReturn = new List<myEntry>(); //Create list that we will return after the calculation finished
            foreach (var entryToCalculate in _entries) //Loop through all entries
            {
                toReturn.Add(GetEntry(entryToCalculate.Name)); //calculate entry values and add them to the list we will return after
            }

            return toReturn; //return list after the whole calculation finished
        }

        private double Calculate(string toCalculate, bool isHeight)
        {
            if (!toCalculate.Contains("+"))
            {
                //String doesn't contain a + that means it has to be a number or a key in our dictionary
                object toConvert = toCalculate; //Set the object we want to convert to double

                var entryCorrespondingToThisValue = _entries.FirstOrDefault(x => x.Name.Equals(toCalculate)); //Check if the string is a reference to another entry
                if (entryCorrespondingToThisValue != null)
                {
                    //It is the name of another object
                    toConvert = isHeight ? entryCorrespondingToThisValue.Height : entryCorrespondingToThisValue.Width; //Set object to convert to the height or width of the object in entries
                }

                try
                {
                    return Convert.ToDouble(toConvert); //try to convert and return if success
                }
                catch (Exception e)
                {
                    //the given height object has the wrong format 
                    //Format: (x + Y + z ...)
                    throw new FormatException();
                }
            }

            //Contains some +

            var spitedString = toCalculate.Split(new char[] {'+'}); //Split
            double sum = 0d; //Whole sum

            foreach (var splited in spitedString) //Loop through all elements
            {
                double toAdd = 0; //To add default = 0

                if (_par.ContainsKey(splited)) //Check if 'splited' is a key in the dictionary
                {
                    //part of object is in the par dictionary so we get the value of it
                    toAdd = _par[splited]; //get value corresponding to key in dictionary
                }
                else
                {
                    //'splited' is not a key in the dictionary

                    object toConvert = splited; //set object to convert

                    var entryCorrespondingToThisValue = _entries.FirstOrDefault(x => x.Name.Equals(splited)); //Does entries contain a reference to this value
                    if (entryCorrespondingToThisValue != null)
                    {
                        //It is a reference
                        toConvert = isHeight ? entryCorrespondingToThisValue.Height : entryCorrespondingToThisValue.Width; //Set to convert to references height or width
                    }

                    try
                    {
                        toAdd = Convert.ToDouble(toConvert); //Try to convert object
                    }
                    catch (Exception e)
                    {
                        //A part of the given height is not a number or is known in the par dictionary
                        throw new FormatException();
                    }
                }
                
                sum += toAdd; //Add after one iteration
            }
            return sum; //return whole sum
        }
        

        public myEntry GetRawEntry(string uniqueName)
        {
            var rawEntry = _entries.FirstOrDefault(x => x.Name.Equals(uniqueName)); //Check for entry in entries by name (unique)
            if (rawEntry == null)
            {
                //Entry is not in the list holding all entries
                throw new UnknownEntry(); //throw an exception
                return null; //Or just return null
            }

            return rawEntry; //return entry
        }
    } 

到此结束,测试并证明它有效:

public void TestIt()
    {
        List<myEntry> entries = new List<myEntry>()
        {
            new myEntry("P1", 0, 0),
            new myEntry("P2", "P1", "P1+Height"),
            new myEntry("P3", "P1+Height", "P2"),
            new myEntry("P4", "P3", "P3+Height"),

        };

        EntryHolder myEntryHolder = new EntryHolder();
        myEntryHolder.Add(entries);
        var calculatedEntries = myEntryHolder.CalculateAllEntries();
    }

这是它的外观图片:

【讨论】:

  • 感谢您的辛勤工作。我尝试了您的代码,它确实适用于我(错误地)我想做的简短示例。我确实想指出我仍然是初学者,并且使用您提供的代码,我尝试通过输入您的解决方案来解决它,但我仍然有点难以理解。此外,我正在尝试以参数名称可以是任何随机字符串的方式解决这个问题。所以 Width 可以是 WallWidthTop 或类似的东西。此外,我试图能够使用 + 或 - 运算符来计算值。不过,现在我要感谢您的大力帮助!
  • @Joop 不客气。如果你想使用 - 只需使用 + 一个负值! :) 例如 P1+-10
  • @Joop 您还可以在 EntryHolder 类的构造函数中名为 _par 的字典中添加任意数量的值,它会自行工作
猜你喜欢
  • 1970-01-01
  • 2020-12-05
  • 1970-01-01
  • 2022-06-21
  • 1970-01-01
  • 1970-01-01
  • 2021-02-20
  • 1970-01-01
  • 2019-12-20
相关资源
最近更新 更多