如何过早退出std.algorithm.iteration.reduce?

时间:2018-12-10 06:53:41

标签: d

我有以下函数findFirstDuplicateFrequency,该函数{正确地实现了用于编程puzzle的算法。

我想推广D的functional features而不是命令式循环,并认为我可以将reduce应用于问题。

我遇到了一个问题,我需要能够对输入序列进行多次(但未知)迭代,并在满足退出条件时退出处理。

AFAICS标准的reduce不能在处理过程中退出,而且我还很难如何进行用于计算退出条件的额外累加器信息。

那么,以(更多)功能方式解决问题的正确(?)惯用D方法是什么?

这是我有史以来第一个D程序,因此也欢迎所有其他评论!

import std.conv: to;

/**
From: https://adventofcode.com/2018/day/1

You notice that the device repeats the same frequency change list over and
over. To calibrate the device, you need to find the first frequency it reaches
twice.

For example, using the same list of changes above, the device would loop as
follows:

    Current frequency  0, change of +1; resulting frequency  1.
    Current frequency  1, change of -2; resulting frequency -1.
    Current frequency -1, change of +3; resulting frequency  2.
    Current frequency  2, change of +1; resulting frequency  3.
    (At this point, the device continues from the start of the list.)
    Current frequency  3, change of +1; resulting frequency  4.
    Current frequency  4, change of -2; resulting frequency  2, which has already been seen.

In this example, the first frequency reached twice is 2. Note that your device
might need to repeat its list of frequency changes many times before a
duplicate frequency is found, and that duplicates might be found while in the
middle of processing the list.

Here are other examples:

    +1, -1 first reaches 0 twice.
    +3, +3, +4, -2, -4 first reaches 10 twice.
    -6, +3, +8, +5, -6 first reaches 5 twice.
    +7, +7, -2, -7, -4 first reaches 14 twice.

What is the first frequency your device reaches twice?
*/
int findFirstDuplicateFrequency(int[] frequencyChanges)
pure
{
  int[int] alreadySeen = [0:1];
  int frequency = 0;

 out_: while(true) {
    foreach(change; frequencyChanges) {
      frequency += change;
      if (int* _ = frequency in alreadySeen) {
        break out_;
      } else {
        alreadySeen[frequency] = 1;
      }
    }
  }

  return frequency;
} unittest {
  int answer = 0;

  answer = findFirstDuplicateFrequency([1, -2, 3, 1]);
  assert(answer == 2, "Got: " ~ to!string(answer));

  answer = findFirstDuplicateFrequency([1, -1]);
  assert(answer == 0, "Got: " ~ to!string(answer));

  answer = findFirstDuplicateFrequency([3, 3, 4, -2, -4]);
  assert(answer == 10, "Got: " ~ to!string(answer));

  answer = findFirstDuplicateFrequency([-6, 3, 8, 5, -6]);
  assert(answer == 5, "Got: " ~ to!string(answer));

  answer = findFirstDuplicateFrequency([7, 7, -2, -7, -4]);
  assert(answer == 14, "Got: " ~ to!string(answer));
}

void main() {}

1 个答案:

答案 0 :(得分:2)

即使根据@ AdamD.Ruppe讲,也没有很大的希望使代码更具“功能性”。我受到@BioTronic的cumulativeFold + until提示的启发,并决定重新考虑。 / p>

很遗憾,我没有办法应用cumulativeFold + until,因为我没有until要求的哨兵值,并且仍然存在与{{1 }}。

浏览standard runtime library reference时,我发现each确实有一个提前退出(也称为部分迭代)选项。我也遇到了std.range.cycle

结合这两个闪亮的新事物,我得出了以下解决方案。 reduce使用findFirstDuplicateFrequencyV2cycle替换第一个版本的命令性循环。我不确定该版本是否更简单,但希望它更“时髦”!

each