C ++ tương đương với mẫu trình tạo Python


117

Tôi có một số mã Python ví dụ mà tôi cần bắt chước trong C ++. Tôi không yêu cầu bất kỳ giải pháp cụ thể nào (chẳng hạn như các giải pháp lợi nhuận dựa trên đồng quy trình, mặc dù chúng cũng sẽ là câu trả lời được chấp nhận), tôi chỉ cần tái tạo ngữ nghĩa theo một cách nào đó.

Python

Đây là một trình tạo trình tự cơ bản, rõ ràng là quá lớn để lưu trữ một phiên bản cụ thể hóa.

def pair_sequence():
    for i in range(2**32):
        for j in range(2**32):
            yield (i, j)

Mục đích là duy trì hai phiên bản của chuỗi trên và lặp lại chúng trong bước bán khóa, nhưng theo nhiều phần. Trong ví dụ dưới đây, first_passsử dụng trình tự các cặp để khởi tạo bộ đệm, và việc second_passtái tạo lại trình tự chính xác tương tự và xử lý lại bộ đệm.

def run():
    seq1 = pair_sequence()
    seq2 = pair_sequence()

    buffer = [0] * 1000
    first_pass(seq1, buffer)
    second_pass(seq2, buffer)
    ... repeat ...

C ++

Điều duy nhất tôi có thể tìm thấy giải pháp trong C ++ là bắt chước yieldvới C ++ coroutines, nhưng tôi không tìm thấy bất kỳ tài liệu tham khảo tốt nào về cách thực hiện điều này. Tôi cũng quan tâm đến các giải pháp thay thế (không chung chung) cho vấn đề này. Tôi không có đủ ngân sách bộ nhớ để giữ một bản sao của chuỗi giữa các lần vượt qua.


Như bạn có thể thấy từ đây stackoverflow.com/questions/3864410/… coroutine không phải là ý kiến ​​hay để triển khai. Bạn không thể làm điều đó với việc đọc có đệm? stackoverflow.com/questions/4685862/…
batbaatar,

Các trình vòng lặp C ++ nên hỗ trợ một cái gì đó như thế này.
Lalaland

Câu trả lời:


79

Trình tạo tồn tại trong C ++, chỉ dưới một tên khác: Trình lặp đầu vào . Ví dụ: đọc từ std::cintương tự như việc có trình tạochar .

Bạn chỉ cần hiểu những gì một máy phát điện làm:

  • có một khối dữ liệu: các biến cục bộ xác định một trạng thái
  • có một phương thức init
  • có một phương pháp "tiếp theo"
  • có một cách để báo hiệu chấm dứt

Trong ví dụ tầm thường của bạn, nó đủ dễ dàng. Về mặt khái niệm:

struct State { unsigned i, j; };

State make();

void next(State&);

bool isDone(State const&);

Tất nhiên, chúng tôi gói nó như một lớp thích hợp:

class PairSequence:
    // (implicit aliases)
    public std::iterator<
        std::input_iterator_tag,
        std::pair<unsigned, unsigned>
    >
{
  // C++03
  typedef void (PairSequence::*BoolLike)();
  void non_comparable();
public:
  // C++11 (explicit aliases)
  using iterator_category = std::input_iterator_tag;
  using value_type = std::pair<unsigned, unsigned>;
  using reference = value_type const&;
  using pointer = value_type const*;
  using difference_type = ptrdiff_t;

  // C++03 (explicit aliases)
  typedef std::input_iterator_tag iterator_category;
  typedef std::pair<unsigned, unsigned> value_type;
  typedef value_type const& reference;
  typedef value_type const* pointer;
  typedef ptrdiff_t difference_type;

  PairSequence(): done(false) {}

  // C++11
  explicit operator bool() const { return !done; }

  // C++03
  // Safe Bool idiom
  operator BoolLike() const {
    return done ? 0 : &PairSequence::non_comparable;
  }

  reference operator*() const { return ij; }
  pointer operator->() const { return &ij; }

  PairSequence& operator++() {
    static unsigned const Max = std::numeric_limts<unsigned>::max();

    assert(!done);

    if (ij.second != Max) { ++ij.second; return *this; }
    if (ij.first != Max) { ij.second = 0; ++ij.first; return *this; }

    done = true;
    return *this;
  }

  PairSequence operator++(int) {
    PairSequence const tmp(*this);
    ++*this;
    return tmp;
  }

private:
  bool done;
  value_type ij;
};

Vì vậy, hum yeah ... có thể là C ++ là một chút dài dòng hơn :)


2
Tôi đã chấp nhận câu trả lời của bạn (cảm ơn!) Vì nó đúng về mặt kỹ thuật cho câu hỏi tôi đã đưa ra. Bạn có bất kỳ gợi ý nào cho các kỹ thuật trong trường hợp trình tự cần được tạo phức tạp hơn không, hoặc tôi chỉ đang đánh một con ngựa chết ở đây với C ++ và thực sự coroutines là cách duy nhất để tổng quát?
Noah Watkins

3
@NoahWatkins: coroutines giúp triển khai dễ dàng khi ngôn ngữ hỗ trợ chúng. Thật không may, C ++ thì không, vì vậy việc lặp lại dễ dàng hơn. Nếu bạn thực sự cần coroutines, bạn thực sự cần một chuỗi đầy đủ để giữ "ngăn xếp" của lệnh gọi hàm của bạn ở bên. Đó chắc chắn là quá mức cần thiết để mở một lon như giun chỉ cho rằng trong ví dụ này, nhưng mileage của bạn có thể thay đổi tùy theo nhu cầu thực tế của bạn.
Matthieu M.

1
Triển khai quy trình đăng quang không dựa trên chuỗi có sẵn trong Boost boost.org/doc/libs/1_57_0/libs/coroutine/doc/html/index.html với đề xuất chuẩn hóa tại đây: open-std.org/jtc1/sc22/ wg21 / docs / giấy tờ / 2014 / n3985.pdf
boycy

2
@boycy: Thực tế có nhiều đề xuất cho coroutines, đáng chú ý là một ngăn xếp ít hơn và ngăn xếp còn lại đầy. Thật khó để bẻ gãy, vì vậy bây giờ tôi đang chờ đợi. Tuy nhiên, trong khi chờ đợi, các coroutines ít ngăn xếp có thể triển khai gần trực tiếp dưới dạng Trình lặp đầu vào (chỉ, không có đường).
Matthieu M.

3
Tuy nhiên, tương tự, các trình vòng lặp không giống như trình tạo.
Огњен Шобајић

52

Trong C ++ có các trình vòng lặp, nhưng việc triển khai một trình vòng lặp không đơn giản: người ta phải tham khảo các khái niệm về trình vòng lặp và thiết kế cẩn thận lớp trình vòng lặp mới để triển khai chúng. Rất may, Boost có mẫu iterator_facade sẽ giúp triển khai các trình vòng lặp và trình tạo tương thích với trình lặp.

Đôi khi một trình điều tra không ngăn xếp có thể được sử dụng để triển khai một trình lặp .

Tái bút Xem thêm bài viết này đề cập đến cả vụ switchhack của Christopher M. Kohlhoff và Boost.Coroutine của Oliver Kowalke. Tác phẩm của Oliver Kowalke là phần tiếp theo trên Boost.Coroutine của Giovanni P. Deretta.

Tái bút Tôi nghĩ bạn cũng có thể viết một loại trình tạo bằng lambdas :

std::function<int()> generator = []{
  int i = 0;
  return [=]() mutable {
    return i < 10 ? i++ : -1;
  };
}();
int ret = 0; while ((ret = generator()) != -1) std::cout << "generator: " << ret << std::endl;

Hoặc với một functor:

struct generator_t {
  int i = 0;
  int operator() () {
    return i < 10 ? i++ : -1;
  }
} generator;
int ret = 0; while ((ret = generator()) != -1) std::cout << "generator: " << ret << std::endl;

PS Đây là một trình tạo được triển khai với các điều tra Mordor :

#include <iostream>
using std::cout; using std::endl;
#include <mordor/coroutine.h>
using Mordor::Coroutine; using Mordor::Fiber;

void testMordor() {
  Coroutine<int> coro ([](Coroutine<int>& self) {
    int i = 0; while (i < 9) self.yield (i++);
  });
  for (int i = coro.call(); coro.state() != Fiber::TERM; i = coro.call()) cout << i << endl;
}

22

Boost.Coroutine2 hiện hỗ trợ nó rất tốt (tôi tìm thấy nó vì tôi muốn giải quyết chính xác yieldvấn đề tương tự ), tôi đang đăng mã C ++ phù hợp với ý định ban đầu của bạn:

#include <stdint.h>
#include <iostream>
#include <memory>
#include <boost/coroutine2/all.hpp>

typedef boost::coroutines2::coroutine<std::pair<uint16_t, uint16_t>> coro_t;

void pair_sequence(coro_t::push_type& yield)
{
    uint16_t i = 0;
    uint16_t j = 0;
    for (;;) {
        for (;;) {
            yield(std::make_pair(i, j));
            if (++j == 0)
                break;
        }
        if (++i == 0)
            break;
    }
}

int main()
{
    coro_t::pull_type seq(boost::coroutines2::fixedsize_stack(),
                          pair_sequence);
    for (auto pair : seq) {
        print_pair(pair);
    }
    //while (seq) {
    //    print_pair(seq.get());
    //    seq();
    //}
}

Trong ví dụ này, pair_sequencekhông có các đối số bổ sung. Nếu nó cần, std::bindhoặc một lambda nên được sử dụng để tạo một đối tượng hàm chỉ nhận một đối số (của push_type), khi nó được truyền cho hàm coro_t::pull_typetạo.


Lưu ý rằng Coroutine2 yêu cầu c ++ 11, không đủ cho visual studio 2013 vì nó chỉ được hỗ trợ một phần.
Weston

5

Tất cả các câu trả lời liên quan đến việc viết trình lặp của riêng bạn là hoàn toàn sai. Những câu trả lời như vậy hoàn toàn bỏ sót điểm của trình tạo Python (một trong những tính năng độc đáo và lớn nhất của ngôn ngữ). Điều quan trọng nhất về máy phát điện là quá trình thực thi bắt đầu từ nơi nó dừng lại. Điều này không xảy ra với các trình lặp. Thay vào đó, bạn phải lưu trữ thông tin trạng thái theo cách thủ công sao cho khi toán tử ++ hoặc toán tử * được gọi lại, thông tin phù hợp đã có ngay từ đầu lệnh gọi hàm tiếp theo. Đây là lý do tại sao việc viết trình lặp C ++ của riêng bạn là một khó khăn lớn; trong khi, máy phát điện thanh lịch và dễ đọc + viết.

Tôi không nghĩ rằng có một tương tự tốt cho trình tạo Python trong C ++ gốc, ít nhất là chưa (có một kẻ lục lọi cho rằng năng suất sẽ hạ cánh trong C ++ 17 ). Bạn có thể nhận được thứ gì đó tương tự bằng cách sử dụng bên thứ ba (ví dụ: đề xuất Tăng cường của Yongwei) hoặc tự làm.

Tôi muốn nói điều gần nhất trong C ++ bản địa là các luồng. Một luồng có thể duy trì một tập hợp các biến cục bộ bị treo và có thể tiếp tục thực thi ở nơi nó đã dừng lại, rất giống với các trình tạo, nhưng bạn cần triển khai thêm một chút cơ sở hạ tầng để hỗ trợ giao tiếp giữa đối tượng trình tạo và trình gọi của nó. Ví dụ

// Infrastructure

template <typename Element>
class Channel { ... };

// Application

using IntPair = std::pair<int, int>;

void yield_pairs(int end_i, int end_j, Channel<IntPair>* out) {
  for (int i = 0; i < end_i; ++i) {
    for (int j = 0; j < end_j; ++j) {
      out->send(IntPair{i, j});  // "yield"
    }
  }
  out->close();
}

void MyApp() {
  Channel<IntPair> pairs;
  std::thread generator(yield_pairs, 32, 32, &pairs);
  for (IntPair pair : pairs) {
    UsePair(pair);
  }
  generator.join();
}

Tuy nhiên, giải pháp này có một số nhược điểm:

  1. Đề bài rất "đắt". Hầu hết mọi người sẽ coi đây là một cách sử dụng các luồng "ngông cuồng", đặc biệt là khi trình tạo của bạn quá đơn giản.
  2. Có một số hành động dọn dẹp mà bạn cần nhớ. Những thứ này có thể được tự động hóa, nhưng bạn sẽ cần nhiều cơ sở hạ tầng hơn nữa, điều này một lần nữa, có thể bị coi là "quá xa hoa". Dù sao, những bước dọn dẹp mà bạn cần là:
    1. out-> close ()
    2. máy phát điện.join ()
  3. Điều này không cho phép bạn dừng máy phát điện. Bạn có thể thực hiện một số sửa đổi để thêm khả năng đó, nhưng nó làm tăng thêm sự lộn xộn cho mã. Nó sẽ không bao giờ sạch sẽ như tuyên bố lợi nhuận của Python.
  4. Ngoài 2, có các bit khác của bảng soạn sẵn cần thiết mỗi khi bạn muốn "khởi tạo" đối tượng trình tạo:
    1. Tham số kênh * ra
    2. Các biến bổ sung trong main: cặp, trình tạo

Bạn đang nhầm lẫn cú pháp với chức năng. Một vài câu trả lời ở trên thực sự cho phép C ++ bắt đầu thực thi từ nơi nó đã dừng lại trong lần gọi cuối cùng. Nó không có gì kỳ diệu. Trên thực tế, Python được triển khai bằng C, vì vậy bất cứ điều gì có thể bằng Python đều có thể bằng C, mặc dù không thuận tiện bằng.
Edy

@edy Có phải điều đó đã được giải quyết trong đoạn đầu tiên không? Anh ấy không tuyên bố rằng chức năng tương đương không thể được tạo ra trong C ++ thông thường, chỉ nói rằng đó là "một nỗi đau lớn".
Kaitain

@Kaitain Câu hỏi ở đây không phải là việc tạo bộ tạo trong C ++ có đau không, mà là liệu có một mẫu để làm như vậy hay không. Những tuyên bố của ông rằng cách tiếp cận "bỏ sót điểm", rằng "thứ gần nhất" là các chủ đề ... chỉ là gây hiểu lầm. Có đau không? Người ta có thể đọc qua các câu trả lời khác và tự quyết định.
Edy

@edy Nhưng điều này không phải là một điểm trống, vì tất cả các ngôn ngữ hoàn chỉnh Turing cuối cùng đều có cùng chức năng? "Bất cứ điều gì có thể xảy ra trong X đều có thể xảy ra trong Y" được đảm bảo là đúng cho tất cả các ngôn ngữ như vậy, nhưng đối với tôi, điều đó dường như không phải là một nhận xét sáng suốt.
Kaitain

@Kaitain Chính vì tất cả các ngôn ngữ hoàn chỉnh Turing được cho là phải có cùng khả năng, do đó câu hỏi làm thế nào để triển khai một tính năng trong một ngôn ngữ khác là chính đáng. Không có gì mà Python không thể được thực hiện bằng một ngôn ngữ khác; câu hỏi là hiệu quả và khả năng bảo trì. Về cả hai phương diện, C ++ sẽ là một lựa chọn tốt (r).
Edy


2

Nếu bạn chỉ cần thực hiện việc này đối với một số lượng tương đối nhỏ các trình tạo cụ thể, bạn có thể triển khai từng trình tạo dưới dạng một lớp, trong đó dữ liệu thành viên tương đương với các biến cục bộ của hàm trình tạo Python. Sau đó, bạn có một hàm tiếp theo trả về thứ tiếp theo mà trình tạo sẽ mang lại, cập nhật trạng thái bên trong như vậy.

Tôi tin rằng điều này về cơ bản tương tự như cách trình tạo Python được triển khai. Sự khác biệt chính là chúng có thể nhớ một phần bù vào bytecode cho hàm bộ tạo như một phần của "trạng thái bên trong", có nghĩa là bộ tạo có thể được viết dưới dạng vòng lặp chứa sản lượng. Thay vào đó, bạn sẽ phải tính giá trị tiếp theo từ giá trị trước đó. Trong trường hợp của bạn pair_sequence, điều đó khá tầm thường. Nó có thể không dành cho máy phát điện phức tạp.

Bạn cũng cần một số cách để chỉ ra sự chấm dứt. Nếu những gì bạn đang trả về là "giống con trỏ" và NULL không phải là một giá trị có thể mang lại hợp lệ, bạn có thể sử dụng con trỏ NULL làm chỉ báo kết thúc. Nếu không, bạn cần một tín hiệu ngoài băng tần.


1

Một cái gì đó như thế này rất giống nhau:

struct pair_sequence
{
    typedef pair<unsigned int, unsigned int> result_type;
    static const unsigned int limit = numeric_limits<unsigned int>::max()

    pair_sequence() : i(0), j(0) {}

    result_type operator()()
    {
        result_type r(i, j);
        if(j < limit) j++;
        else if(i < limit)
        {
          j = 0;
          i++;
        }
        else throw out_of_range("end of iteration");
    }

    private:
        unsigned int i;
        unsigned int j;
}

Việc sử dụng toán tử () chỉ là câu hỏi về những gì bạn muốn làm với trình tạo này, bạn cũng có thể xây dựng nó dưới dạng một luồng và đảm bảo rằng nó thích ứng với istream_iterator chẳng hạn.


1

Sử dụng range-v3 :

#include <iostream>
#include <tuple>
#include <range/v3/all.hpp>

using namespace std;
using namespace ranges;

auto generator = [x = view::iota(0) | view::take(3)] {
    return view::cartesian_product(x, x);
};

int main () {
    for (auto x : generator()) {
        cout << get<0>(x) << ", " << get<1>(x) << endl;
    }

    return 0;
}

0

Một cái gì đó như thế này :

Ví dụ sử dụng:

using ull = unsigned long long;

auto main() -> int {
    for (ull val : range_t<ull>(100)) {
        std::cout << val << std::endl;
    }

    return 0;
}

Sẽ in các số từ 0 đến 99


0

Hôm nay, tôi cũng đang tìm cách triển khai bộ sưu tập dễ dàng trong C ++ 11. Thực sự tôi rất thất vọng, vì mọi thứ tôi tìm thấy quá xa so với những thứ như trình tạo python, toán tử năng suất C # ... hoặc quá phức tạp.

Mục đích là tạo ra bộ sưu tập sẽ chỉ phát ra các mục của nó khi nó được yêu cầu.

Tôi muốn nó như thế này:

auto emitter = on_range<int>(a, b).yield(
    [](int i) {
         /* do something with i */
         return i * 2;
    });

Tôi đã tìm thấy bài đăng này, câu trả lời hay nhất của IMHO là về boost.coroutine2, của Yongwei Wu . Vì nó là gần nhất với những gì tác giả muốn.

Nó đáng để học các chương trình học tăng cường .. Và có lẽ tôi sẽ làm vào cuối tuần. Nhưng cho đến nay tôi đang sử dụng triển khai rất nhỏ của mình. Hy vọng nó sẽ giúp ích cho người khác.

Dưới đây là ví dụ về việc sử dụng và sau đó là triển khai.

Ví dụ.cpp

#include <iostream>
#include "Generator.h"
int main() {
    typedef std::pair<int, int> res_t;

    auto emitter = Generator<res_t, int>::on_range(0, 3)
        .yield([](int i) {
            return std::make_pair(i, i * i);
        });

    for (auto kv : emitter) {
        std::cout << kv.first << "^2 = " << kv.second << std::endl;
    }

    return 0;
}

Máy phát điện.h

template<typename ResTy, typename IndexTy>
struct yield_function{
    typedef std::function<ResTy(IndexTy)> type;
};

template<typename ResTy, typename IndexTy>
class YieldConstIterator {
public:
    typedef IndexTy index_t;
    typedef ResTy res_t;
    typedef typename yield_function<res_t, index_t>::type yield_function_t;

    typedef YieldConstIterator<ResTy, IndexTy> mytype_t;
    typedef ResTy value_type;

    YieldConstIterator(index_t index, yield_function_t yieldFunction) :
            mIndex(index),
            mYieldFunction(yieldFunction) {}

    mytype_t &operator++() {
        ++mIndex;
        return *this;
    }

    const value_type operator*() const {
        return mYieldFunction(mIndex);
    }

    bool operator!=(const mytype_t &r) const {
        return mIndex != r.mIndex;
    }

protected:

    index_t mIndex;
    yield_function_t mYieldFunction;
};

template<typename ResTy, typename IndexTy>
class YieldIterator : public YieldConstIterator<ResTy, IndexTy> {
public:

    typedef YieldConstIterator<ResTy, IndexTy> parent_t;

    typedef IndexTy index_t;
    typedef ResTy res_t;
    typedef typename yield_function<res_t, index_t>::type yield_function_t;
    typedef ResTy value_type;

    YieldIterator(index_t index, yield_function_t yieldFunction) :
            parent_t(index, yieldFunction) {}

    value_type operator*() {
        return parent_t::mYieldFunction(parent_t::mIndex);
    }
};

template<typename IndexTy>
struct Range {
public:
    typedef IndexTy index_t;
    typedef Range<IndexTy> mytype_t;

    index_t begin;
    index_t end;
};

template<typename ResTy, typename IndexTy>
class GeneratorCollection {
public:

    typedef Range<IndexTy> range_t;

    typedef IndexTy index_t;
    typedef ResTy res_t;
    typedef typename yield_function<res_t, index_t>::type yield_function_t;
    typedef YieldIterator<ResTy, IndexTy> iterator;
    typedef YieldConstIterator<ResTy, IndexTy> const_iterator;

    GeneratorCollection(range_t range, const yield_function_t &yieldF) :
            mRange(range),
            mYieldFunction(yieldF) {}

    iterator begin() {
        return iterator(mRange.begin, mYieldFunction);
    }

    iterator end() {
        return iterator(mRange.end, mYieldFunction);
    }

    const_iterator begin() const {
        return const_iterator(mRange.begin, mYieldFunction);
    }

    const_iterator end() const {
        return const_iterator(mRange.end, mYieldFunction);
    }

private:
    range_t mRange;
    yield_function_t mYieldFunction;
};

template<typename ResTy, typename IndexTy>
class Generator {
public:
    typedef IndexTy index_t;
    typedef ResTy res_t;
    typedef typename yield_function<res_t, index_t>::type yield_function_t;

    typedef Generator<ResTy, IndexTy> mytype_t;
    typedef Range<IndexTy> parent_t;
    typedef GeneratorCollection<ResTy, IndexTy> finalized_emitter_t;
    typedef  Range<IndexTy> range_t;

protected:
    Generator(range_t range) : mRange(range) {}
public:
    static mytype_t on_range(index_t begin, index_t end) {
        return mytype_t({ begin, end });
    }

    finalized_emitter_t yield(yield_function_t f) {
        return finalized_emitter_t(mRange, f);
    }
protected:

    range_t mRange;
};      

0

Câu trả lời này hoạt động trong C (và do đó tôi nghĩ cũng hoạt động trong c ++)

#include <stdio.h>

const uint64_t MAX = 1ll<<32;

typedef struct {
    uint64_t i, j;
} Pair;

Pair* generate_pairs()
{
    static uint64_t i = 0;
    static uint64_t j = 0;
    
    Pair p = {i,j};
    if(j++ < MAX)
    {
        return &p;
    }
        else if(++i < MAX)
    {
        p.i++;
        p.j = 0;
        j = 0;
        return &p;
    }
    else
    {
        return NULL;
    }
}

int main()
{
    while(1)
    {
        Pair *p = generate_pairs();
        if(p != NULL)
        {
            //printf("%d,%d\n",p->i,p->j);
        }
        else
        {
            //printf("end");
            break;
        }
    }
    return 0;
}

Đây là cách đơn giản, không hướng đối tượng để bắt chước một trình tạo. Điều này đã làm việc như mong đợi đối với tôi.


-1

Cũng giống như một hàm mô phỏng khái niệm ngăn xếp, trình tạo mô phỏng khái niệm hàng đợi. Phần còn lại là ngữ nghĩa.

Một lưu ý nhỏ là bạn luôn có thể mô phỏng một hàng đợi với một ngăn xếp bằng cách sử dụng một ngăn xếp các hoạt động thay vì dữ liệu. Điều đó thực tế có nghĩa là bạn có thể thực hiện một hành vi giống như hàng đợi bằng cách trả về một cặp, giá trị thứ hai của chúng có hàm tiếp theo được gọi hoặc cho biết rằng chúng ta đã hết giá trị. Nhưng điều này tổng quát hơn những gì lợi nhuận so với lợi nhuận. Nó cho phép mô phỏng một hàng đợi của bất kỳ giá trị nào hơn là các giá trị đồng nhất mà bạn mong đợi từ trình tạo, nhưng không giữ một hàng đợi nội bộ đầy đủ.

Cụ thể hơn, vì C ++ không có trừu tượng tự nhiên cho hàng đợi, bạn cần sử dụng các cấu trúc triển khai hàng đợi bên trong. Vì vậy, câu trả lời đưa ra ví dụ với các trình vòng lặp là một cách triển khai tốt của khái niệm.

Điều này thực tế có nghĩa là bạn có thể triển khai một cái gì đó với chức năng hàng đợi đơn giản nếu bạn chỉ muốn một cái gì đó nhanh chóng và sau đó sử dụng các giá trị của hàng đợi giống như bạn sử dụng các giá trị được tạo ra từ trình tạo.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.