我一直在用 C# 来创建一个软件(供伪个人使用)的想法,但遇到了实现问题。嗯......也许它们是我不知道的设计问题,但我就是想不通。
思想、期望和一般伪算法
考虑我们有以下“数据库”:
Foo + Bar = Foobar
Baz + Qux = Bazqux
Foobar + Bazqux = Foobarbazqux
这些是创建上述项目的食谱。使用一个和一个,我们创建了一个结果,该结果可以进一步成为另一个配方的成分。Foo
Bar
Foobar
现在认为我们需要弄清楚该项目的完整配方Foobarbazqux
。对于人类的思想和智力来说,这相对容易:
We need Foobarbazqux
Foobarbazqux = Foobar + Bazqux
Foobar = Foo + Bar
Bazqux = Baz + Qux
Foobarbazqux = Foo + Bar + Baz + Qux
当然,如果并且当我们拥有所有可用的成分时,我们可以“运行”配方并按照上面几段的顺序执行每个配方来创建项目。(就像:我们首先创建Foobar
,然后Bazqux
,然后将两者结合以获得最终结果。)
但在实时,数据库要大得多。我想这就是计算机应该发挥作用的地方。使用工作软件,机器可以轻松找到所有成分和执行(制作)步骤并将其提供给用户,因此我们不需要通读所有条目手。
实施(...尝试)
我曾想过使用 C# 来实现这个软件。命令行项目,没有什么闪亮的东西,只是纯粹stdio
的 .
首先,当然,我需要定义item。(注意:现在我正在考虑使用结构,但如果需要,我可以将它们“升级”为类。“数据库”是从稍后将制作的某种顺序文本文件初始化的。“数据库”没有改变或在执行期间或之后以任何方式编写,一旦加载,它就是只读的。)当然,我需要定义recipe。
struct Item
{
public string Name;
}
struct Recipe
{
public Item Result;
public Item[] Ingredients;
}
这两个结构保存在(默认类)的通用static
List<>
字段中。Program
在入口点,我们定义了一些项目和一些测试配方:
items.Add(new Item { Name = "Foo" });
items.Add(new Item { Name = "Bar" });
items.Add(new Item { Name = "Baz" });
items.Add(new Item { Name = "Qux" });
items.Add(new Item { Name = "Foobar" });
items.Add(new Item { Name = "Bazqux" });
items.Add(new Item { Name = "Foobarbazqux" });
recipes.Add(new Recipe
{
Result = GetItem("Foobar"),
Ingredients = new Item[] { GetItem("Foo"), GetItem("Bar") }
});
recipes.Add(new Recipe
{
Result = GetItem("Bazqux"),
Ingredients = new Item[] { GetItem("Baz"), GetItem("Qux") }
});
recipes.Add(new Recipe
{
Result = GetItem("Foobarbazqux"),
Ingredients = new Item[] { GetItem("Foobar"), GetItem("Bazqux") }
});
因为项目是由他们的Name
领域主键,GetItem
只是一个List.Find<>
快捷方式:
private static Item GetItem(string _name)
{
return Program.Items.Find(match => match.Name == _name);
}
所以现在,数据库已经设置好了。我意识到我需要使用某种递归方法来获取所有成分,但这就是我遇到问题的地方。
考虑以下第一次尝试(这不是递归的,只会“深一层”):
string search = "Foobarbazqux";
SearchRecipe(search);
private static void SearchRecipe(string _search)
{
List<Recipe> item_recipes = Program.Recipes.FindAll(match => match.result.Name == GetItem(search).Name);
foreach (Recipe recp in item_recipes)
{
Console.WriteLine("-------------");
Console.WriteLine("Ingredients: ");
foreach (Item ingredient in recp.Ingredients)
{
Console.WriteLine(ingredient.Name);
}
}
}
这会产生以下输出,即,没有递归,非常好并且可以预期:
-------------
Ingredients:
Foobar
Bazqux
所以对于递归,我修改了这样的代码:
foreach (Item ingredient in recp.Ingredients)
{
+++ if (recipes.FindAll(match2 => match2.Result.name == Ingredient.name).Count != 0)
+++ {
+++ SearchRecipe(ingredient.Name);
+++ }
Console.WriteLine(ingredient.Name);
}
现在输出如下:
| -------------
| Ingredients:
* -------------
* Ingredients:
* Foo
* Bar
| Foobar
@ -------------
@ Ingredients:
@ Baz
@ Qux
| Bazqux
当我现在理解我的代码时,我看到了会发生什么。标有 的行*
是 的递归Foobar
,@
是 的递归Bazqux
并且|
是原始方法调用的输出。但这不是……有点像我想要实现的输出,因为它不容易被理解并且……通常是不正确的。
预期输出和问题
我可视化的输出类似于以下内容(当然“额外”状态行是可忽略的):
Searching for: Foobarbazqux
1 recipe found.
-----
1 Foobarbazqux = 1 Foobar + 1 Bazqux
1 Foobar = 1 Foo + 1 Bar
1 Bazqux = 1 Baz + 1 Qux
1 Foobarbazqux = 1 Foo + 1 Bar + 1 Baz + 1 Qux
1 Foo + 1 Bar => 1 Foobar
1 Baz + 1 Qux => 1 Bazqux
1 Foobar + 1 Bazqux => 1 Foobarbazqux
-----
Exit.
这就是我所说的打破项目创建配方链......或者......我不称之为,但想象一下,参考问题的标题。所以程序的工作原理是:
- 用户输入他们正在搜索的“最终”项目
Foobarbazqux
(在我们的示例中) - 该程序对数据库进行了足够多次的破坏,因此首先找到了搜索项目的成分,然后递归(?)更深,直到它只找到无法从其他人“制作”的亚原子成分:它们不是任何.
Result
Recipe
- (同时工作)输出显示。
注意:更适合真实场景,但存在额外的实施差距:许多项目都有多个可以创建它们的配方。
我想寻求的是建议和帮助。我慢慢地想说服自己这个程序是不可写的(这只是某种绝望的强烈抗议)并且我的想法存在设计问题。但我仍然希望,在界限内,我们不需要花哨的、尚未创建的人工智能系统来解决这个……有点低估的问题。
谢谢你。