将带有 yield(string) 的 python 函数翻译成 C++
translate python function with yield(string) to C++
我正在将 python 函数转换为 C++ 函数;这个函数使用了一个我不知道如何翻译的yield(string)
语句。
这里是整个故事......我有一个特定的函数,它读取一个包含数据行的输入 ASCII 文件 filename
,并将文件的内容放入 std::vector
。当读取这个输入文件时(在另一个函数方面,如下所示),我需要跳转一堆行(5 或 6,这取决于输入文件的名称),为此,我定义了一个函数, file_struct_generator
,它将数据线标记为“数据”,将我不需要的数据线标记为“未使用”。我在 C++ 中需要类似于此函数的东西,特别是,我需要类似于 yield(string)
的东西(注意,string
!),但当然是在 C++ 中。在这里,我向您展示了我需要从 python“翻译”到 C++ 的代码行。如何在 C++ 中重写 yield("unused")
和 yield("data")
?或者如果 yield
在 C++ 中不可用,我可以在 C++ 中使用不同的东西作为生成器编写类似的函数吗?谢谢你帮助我!
def file_struct_generator(filename):
if "more" in filename:
bad_line_number = 5
else:
bad_line_number = 6
yield("data")
for i in range(bad_line_number):
yield("unused")
while True:
yield("data")
编辑:
我不使用 C++20,而是使用 C++11。我尝试了@Arthur Tacca 代码,它对我有用,谢谢大家。
translate python function with yield(string) to C++
在 C++20 之前,我认为没有简单、准确的翻译。
另一种方法是编写一个接受仿函数的函数模板:
template<class Yield>
void file_struct_generator(const std::string& filename, const Yield& yield)
{
// ...
yield("unused")
// ...
通过这种方式,调用者可以提供一个仿函数,以他们希望使用的方式处理输出。例如,他们可以提供打印值的方法,或将值存储在容器中的方法。
重要的是,这个替代方案不是惰性的,您不能像 python 代码中那样出现无限循环。这是否是一个有用的翻译取决于生成器在 Python 中的使用方式。这对于消耗整个序列的情况很有用。
我认为这将是 C++20 中的直接翻译:
generator<std::string>
file_struct_generator(const std::string& filename)
{
bad_line_number = filename.find("more") != std::string::npos
? 5
: 6;
co_yield "data";
for (int i : std::views::iota(0, bad_line_number))
co_yield "unused";
for(;;)
co_yield "data";
}
由于缺少符合标准的编译器,因此未经过测试。此外,该标准不提供 generator
类型。
从 C++20 开始,您可以使用 co-routines,您可以使用一些 thread_local 变量(静态)实现与 python 片段类似的功能:
std::string file_struct_generator(std::string filename)
{
thread_local int bad_line_number = filename.find("more") != std::string::npos ? 5 : 6;
thread_local int i = 0;
if (i++ && i <= bad_line_number)
return "unused";
return "data";
}
请注意,正如 ArthurTacca 在评论中指出的那样,这个函数实际上不能用不同的 file-name 调用,或者至少,如果你这样做,它不会开始一个新的循环.
这是一个重现生成器功能的状态机。
#include <stdexcept>
#include <string>
#include <iostream>
class FileStructIterator {
public:
explicit FileStructIterator(const std::string& filename):
m_filename(filename), m_state(STATE_START)
{}
std::string getNext() {
switch (m_state) {
case STATE_START:
m_state = STATE_LOOP1;
if (m_filename.find("more") != std::string::npos) {
m_badLineNumber = 5;
} else {
m_badLineNumber = 6;
}
m_i = 0;
return "data";
case STATE_LOOP1:
++m_i;
if (m_i >= m_badLineNumber) {
m_state = STATE_LOOP2;
}
return "unused";
case STATE_LOOP2:
return "data";
default:
throw std::logic_error("bad state");
}
}
private:
std::string m_filename;
enum State { STATE_START, STATE_LOOP1, STATE_LOOP2 };
State m_state;
size_t m_badLineNumber;
size_t m_i;
};
这是一个使用它的例子(在这种情况下我将输出限制为前 10 个结果,因此它不会永远循环)。
int main() {
auto it = FileStructIterator("nomore.txt");
for (int i = 0; i < 10; ++i) {
std::string nextValue = it.getNext();
std::cout << nextValue << "\n";
}
}
在 C++20 中,这将使用 co_yield
。在此之前你可以写一个迭代器。
class file_struct_iterator : public std::iterator<std::input_iterator_tag, std::string, std::ptrdiff_t, const std::string *, std::string> {
enum class State {
initial,
bad_lines,
remainder
}
State state = State::initial;
size_t bad_line_number = 0;
public:
file_struct_iterator(std::string filename)
: bad_line_number((filename.find("more) != filename.end()) ? 5 : 6)
{}
std::string operator*() {
switch (state) {
case State::initial:
case State::remainder: return "data";
case State::bad_lines: return "unused";
}
}
file_struct_iterator& operator++() {
switch (state) {
case State::initial: state = State::bad_lines; break;
case State::bad_lines: if (--bad_line_number == 0) { state = remainder; } break;
case State::remainder: break;
}
return *this;
}
file_struct_iterator operator++(int) {
file_struct_iterator retval = *this;
++(*this);
return retval;
}
bool operator==(file_struct_iterator other) const {
return (state == other.state) && (bad_line_number == other.bad_line_number);
}
bool operator!=(file_struct_iterator other) const {
return !(*this == other);
}
};
我正在将 python 函数转换为 C++ 函数;这个函数使用了一个我不知道如何翻译的yield(string)
语句。
这里是整个故事......我有一个特定的函数,它读取一个包含数据行的输入 ASCII 文件 filename
,并将文件的内容放入 std::vector
。当读取这个输入文件时(在另一个函数方面,如下所示),我需要跳转一堆行(5 或 6,这取决于输入文件的名称),为此,我定义了一个函数, file_struct_generator
,它将数据线标记为“数据”,将我不需要的数据线标记为“未使用”。我在 C++ 中需要类似于此函数的东西,特别是,我需要类似于 yield(string)
的东西(注意,string
!),但当然是在 C++ 中。在这里,我向您展示了我需要从 python“翻译”到 C++ 的代码行。如何在 C++ 中重写 yield("unused")
和 yield("data")
?或者如果 yield
在 C++ 中不可用,我可以在 C++ 中使用不同的东西作为生成器编写类似的函数吗?谢谢你帮助我!
def file_struct_generator(filename):
if "more" in filename:
bad_line_number = 5
else:
bad_line_number = 6
yield("data")
for i in range(bad_line_number):
yield("unused")
while True:
yield("data")
编辑: 我不使用 C++20,而是使用 C++11。我尝试了@Arthur Tacca 代码,它对我有用,谢谢大家。
translate python function with yield(string) to C++
在 C++20 之前,我认为没有简单、准确的翻译。
另一种方法是编写一个接受仿函数的函数模板:
template<class Yield>
void file_struct_generator(const std::string& filename, const Yield& yield)
{
// ...
yield("unused")
// ...
通过这种方式,调用者可以提供一个仿函数,以他们希望使用的方式处理输出。例如,他们可以提供打印值的方法,或将值存储在容器中的方法。
重要的是,这个替代方案不是惰性的,您不能像 python 代码中那样出现无限循环。这是否是一个有用的翻译取决于生成器在 Python 中的使用方式。这对于消耗整个序列的情况很有用。
我认为这将是 C++20 中的直接翻译:
generator<std::string>
file_struct_generator(const std::string& filename)
{
bad_line_number = filename.find("more") != std::string::npos
? 5
: 6;
co_yield "data";
for (int i : std::views::iota(0, bad_line_number))
co_yield "unused";
for(;;)
co_yield "data";
}
由于缺少符合标准的编译器,因此未经过测试。此外,该标准不提供 generator
类型。
从 C++20 开始,您可以使用 co-routines,您可以使用一些 thread_local 变量(静态)实现与 python 片段类似的功能:
std::string file_struct_generator(std::string filename)
{
thread_local int bad_line_number = filename.find("more") != std::string::npos ? 5 : 6;
thread_local int i = 0;
if (i++ && i <= bad_line_number)
return "unused";
return "data";
}
请注意,正如 ArthurTacca 在评论中指出的那样,这个函数实际上不能用不同的 file-name 调用,或者至少,如果你这样做,它不会开始一个新的循环.
这是一个重现生成器功能的状态机。
#include <stdexcept>
#include <string>
#include <iostream>
class FileStructIterator {
public:
explicit FileStructIterator(const std::string& filename):
m_filename(filename), m_state(STATE_START)
{}
std::string getNext() {
switch (m_state) {
case STATE_START:
m_state = STATE_LOOP1;
if (m_filename.find("more") != std::string::npos) {
m_badLineNumber = 5;
} else {
m_badLineNumber = 6;
}
m_i = 0;
return "data";
case STATE_LOOP1:
++m_i;
if (m_i >= m_badLineNumber) {
m_state = STATE_LOOP2;
}
return "unused";
case STATE_LOOP2:
return "data";
default:
throw std::logic_error("bad state");
}
}
private:
std::string m_filename;
enum State { STATE_START, STATE_LOOP1, STATE_LOOP2 };
State m_state;
size_t m_badLineNumber;
size_t m_i;
};
这是一个使用它的例子(在这种情况下我将输出限制为前 10 个结果,因此它不会永远循环)。
int main() {
auto it = FileStructIterator("nomore.txt");
for (int i = 0; i < 10; ++i) {
std::string nextValue = it.getNext();
std::cout << nextValue << "\n";
}
}
在 C++20 中,这将使用 co_yield
。在此之前你可以写一个迭代器。
class file_struct_iterator : public std::iterator<std::input_iterator_tag, std::string, std::ptrdiff_t, const std::string *, std::string> {
enum class State {
initial,
bad_lines,
remainder
}
State state = State::initial;
size_t bad_line_number = 0;
public:
file_struct_iterator(std::string filename)
: bad_line_number((filename.find("more) != filename.end()) ? 5 : 6)
{}
std::string operator*() {
switch (state) {
case State::initial:
case State::remainder: return "data";
case State::bad_lines: return "unused";
}
}
file_struct_iterator& operator++() {
switch (state) {
case State::initial: state = State::bad_lines; break;
case State::bad_lines: if (--bad_line_number == 0) { state = remainder; } break;
case State::remainder: break;
}
return *this;
}
file_struct_iterator operator++(int) {
file_struct_iterator retval = *this;
++(*this);
return retval;
}
bool operator==(file_struct_iterator other) const {
return (state == other.state) && (bad_line_number == other.bad_line_number);
}
bool operator!=(file_struct_iterator other) const {
return !(*this == other);
}
};