4

我发现自己必须不断地为了不同的目的来回更改和调整旧代码,但偶尔为了实现两个版本之前的相同目的。

一个例子是处理素数的函数。有时我需要的是 n 个素数的列表。有时我需要的是第 n 个素数。也许我会在以后的功能中遇到第三个需求。

尽管我必须执行相同的过程但只返回不同的值,但无论如何我都会这样做。我认为必须有更好的方法来做到这一点,而不是不断地更改相同的代码。我想出的可能替代方案是:

  1. 返回一个元组或一个列表,但这似乎有点混乱,因为其中会有各种数据类型,包括数千个项目的列表。

  2. 使用输入语句来指导代码,虽然我宁愿让它在我单击运行时为我做所有事情。

  3. 弄清楚如何利用类特性来返回类属性并在我需要的地方访问它们。这对我来说似乎是最干净的解决方案,但我不确定,因为我还是新手。

  4. 只需为每个可重用函数制作五个版本。

我不想成为一个糟糕的程序员,那么哪个选择是正确的选择?或者,也许我可以做一些我没有想到的事情。

4

3 回答 3

6

模块化、可重用的代码

你的问题确实很重要。这在程序员的日常生活中很重要。这是一个问题:

我的代码可以重用吗?

如果不是,您将遇到代码冗余,在多个地方有相同的代码行。这是 bug 的最佳起点。想象一下,你想以某种方式改变行为,例如,因为你发现了一个潜在的问题。然后你在一个地方改变它,但你会忘记第二个位置。尤其是当您的代码达到 1,000、10,0000 或 100,000 行代码时。

它在SRP中进行了总结,即单一职责原则。它指出每个类(也适用于函数)应该只有一个决定,即它“应该只做一件事”。如果一个函数做不止一件事,你应该把它分成更小的块,更小的任务。

每次遇到(或编写)包含超过 10 或 20 行(实际)代码的函数时,您都应该持怀疑态度。这样的功能很少坚持这个原则。

对于您的示例,您可以将其标识为单个任务:

  1. 一个一个地生成素数(生成意味着yield为我使用)
  2. 收集n素数。使用 1. 并将它们放入列表中
  3. 得到n素数。使用 1.,但不保存每个数字,只是等待nth。不像 2. 那样消耗内存。
  4. 查找素数对:使用 1.,记住前一个数,如果与当前数的差为 2,则产生这对
  5. 收集所有素数对:使用 4. 并将它们放入列表中
  6. ...
  7. ...

该列表是可扩展的,您可以在任何级别重复使用它。每个函数的代码不会超过 10 行,而且您不会每次都重新发明轮子。

将它们全部放入一个模块中,并从每个脚本中使用它来解决与素数相关的欧拉问题。

一般来说,我为我的欧拉问题脚本创建了一个小型库。你真的可以习惯在“Project Euler”中编写可重用的代码。

关键字参数

您没有提到的另一个选项(据我所知)是使用可选关键字参数。如果你认为小的原子函数太复杂(尽管我真的坚持你应该习惯它),你可以添加一个关键字参数来控制返回值。例如,在某些scipy函数中有一个参数full_output,它接受一个布尔值。如果为 False(默认),则仅返回最重要的信息(例如,优化值),如果为 True,则还返回一些补充信息,例如,优化执行得有多好以及需要多少次迭代才能收敛。

你可以定义一个参数output_mode,可能的值"list""last"或者其他任何东西。

推荐

坚持使用小的、可重用的代码块。习惯这一点是您可以在“Project Euler”中获得的最有价值的东西之一。

评论

如果您尝试实现我为可重用函数提出的模式,您可能会在第 1 点立即遇到问题:如何为此创建生成器样式的函数?例如,如果您使用筛法。但这还不错。

于 2013-02-08T07:21:59.693 回答
0

我的猜测,创建包含以下内容的模块:

  1. 私有核心函数(例如:返回第 n 个第一个素数的列表,甚至更一般的东西)
  2. 几个使用核心一并以不同方式准备输出的包装器/实用程序函数。(例如:第 n 个素数)
于 2013-02-08T06:46:40.467 回答
0

尽量减少你的函数,并重用它们。

例如,您可能有一个next_primen_primesand重复调用的函数n_th_prime

这也使您的代码更易于维护,就好像您想出了一种更有效的方法来计算素数一样,您所做的就是更改next_prime.

此外,您应该使您的输出尽可能中立。如果你的函数返回多个值,它应该返回一个list或一个生成器,而不是一个逗号分隔的字符串。

于 2013-02-08T07:19:33.227 回答