使用GO GENRICS实现一系列处理器
我正在尝试在GO中实现一种简单的处理管道,每个处理器都有确定的输入和输出类型,以及将当前处理器输出类型作为输入的后继处理器列表,并且可能具有其自己的后继处理器。
我正在遇到有关如何将后继处理器添加到当前一个问题的问题,无论其输出类型如何。我尝试使用作为通配符类型,就像我在java中使用?
一样,但是Go没有它。
我走的是:
type Processor[InputType any, OutputType any] struct {
nextProcessors []*Processor[OutputType, any]
ProcessingFunction func(InputType) OutputType
}
func (b *Processor[InputType, OutputType]) Process(input InputType) {
result := b.ProcessingFunction(input)
for _, nextProcessor := range b.nextProcessors {
nextProcessor.Process(result)
}
}
func (b *Processor[InputType, OutputType]) AddOutputProcessor(p *Processor[OutputType, any]) {
b.nextProcessors = append(b.nextProcessors, p)
}
func main() {
outputer := Processor[int, string]{ProcessingFunction: func(input int) string {
print(input)
return string(input)
}}
doubler := Processor[int, int]{ProcessingFunction: func(input int) int { return input * 2 }}
rng := Processor[int, int]{ProcessingFunction: func(input int) int { return rand.Intn(input) }}
rng.AddOutputProcessor(&doubler)
doubler.AddOutputProcessor(&outputer)
rng.Process(20)
}
这给了我汇编错误:
无法使用& doubleer(类型 *处理器[int,int])作为类型 *处理器[int,任何]
是否可以忽略后继处理器的输出类型?还是我应该采取不同的方式?我只想确保后继处理器可以接受正确的输入类型。
作为参考,这是Java中的接口定义,它按照我打算的方式进行操作。
public interface Processor<InputType, OutputType> {
void addOutputProcessor(Processor<OutputType, ?> outputProcessor);
void process(InputType input);
}
public class Pipeline {
private abstract class BaseProcessor<InputType, OutputType> implements Processor<InputType, OutputType> {
List<Processor<OutputType, ?>> nextProcessors = new ArrayList<>();
abstract OutputType processHelper(InputType input);
@Override
public void addOutputProcessor(Processor<OutputType, ?> outputProcessor) {
nextProcessors.add(outputProcessor);
}
@Override
public void process(InputType input) {
OutputType result = processHelper(input);
for (Processor<OutputType, ?> nextProcessor : nextProcessors) {
nextProcessor.process(result);
}
}
}
private class RandomNumberGenerator extends BaseProcessor<Integer, Integer> {
@Override
Integer processHelper(Integer input) {
int generatedNumber = new Random().nextInt(input);
return generatedNumber;
}
}
private class IncrementProcessor extends BaseProcessor<Integer, Integer> {
@Override
Integer processHelper(Integer input) {
return input + 1;
}
}
private class DoubleProcessor extends BaseProcessor<Integer, Integer> {
@Override
Integer processHelper(Integer input) {
return input * 2;
}
}
private class Outputer extends BaseProcessor<Integer, String> {
String name;
public Outputer(String name) {
this.name = name;
}
@Override
String processHelper(Integer input) {
String output = String.format("Pipeline %s result: %d", name, input);
System.out.println(output);
return output;
}
}
public void buildAndRunPipeline() {
Processor<Integer, String> doublingOutputter = new Outputer("Doubling");
Processor<Integer, String> incrementingOutputter = new Outputer("Incrementing");
Processor<Integer, String> rngOutputter = new Outputer("Generating number");
Processor<Integer, Integer> doubler = new DoubleProcessor();
doubler.addOutputProcessor(doublingOutputter);
Processor<Integer, Integer> incrementer = new IncrementProcessor();
incrementer.addOutputProcessor(incrementingOutputter);
Processor<Integer, Integer> starter = new RandomNumberGenerator();
starter.addOutputProcessor(rngOutputter);
starter.addOutputProcessor(doubler);
starter.addOutputProcessor(incrementer);
starter.process(20);
}
public static void main(String[] args) {
Pipeline p = new Pipeline();
p.buildAndRunPipeline();
}
}
I am trying to implement a kind of simple processing pipeline in Go, where each processor has a determined input and output type, and a list of successor processors that take current processor output type as input, and may have successor processors of their own.
I am running into issues on how to add successor processors to the current one, regardless of their output type. I tried using any
as a wildcard type like I would do with ?
in Java, but Go is not having it.
What I have in Go is this:
type Processor[InputType any, OutputType any] struct {
nextProcessors []*Processor[OutputType, any]
ProcessingFunction func(InputType) OutputType
}
func (b *Processor[InputType, OutputType]) Process(input InputType) {
result := b.ProcessingFunction(input)
for _, nextProcessor := range b.nextProcessors {
nextProcessor.Process(result)
}
}
func (b *Processor[InputType, OutputType]) AddOutputProcessor(p *Processor[OutputType, any]) {
b.nextProcessors = append(b.nextProcessors, p)
}
func main() {
outputer := Processor[int, string]{ProcessingFunction: func(input int) string {
print(input)
return string(input)
}}
doubler := Processor[int, int]{ProcessingFunction: func(input int) int { return input * 2 }}
rng := Processor[int, int]{ProcessingFunction: func(input int) int { return rand.Intn(input) }}
rng.AddOutputProcessor(&doubler)
doubler.AddOutputProcessor(&outputer)
rng.Process(20)
}
Which gives me a compilation error:
cannot use &doubler (value of type *Processor[int, int]) as type *Processor[int, any]
Is there a way to ignore the output type of the successor processor? Or should I maybe go a different way about it? I would just like to make sure that successor processors can accept the right type of input.
For reference, here is the interface definition in Java that works the way I intend it to.
public interface Processor<InputType, OutputType> {
void addOutputProcessor(Processor<OutputType, ?> outputProcessor);
void process(InputType input);
}
public class Pipeline {
private abstract class BaseProcessor<InputType, OutputType> implements Processor<InputType, OutputType> {
List<Processor<OutputType, ?>> nextProcessors = new ArrayList<>();
abstract OutputType processHelper(InputType input);
@Override
public void addOutputProcessor(Processor<OutputType, ?> outputProcessor) {
nextProcessors.add(outputProcessor);
}
@Override
public void process(InputType input) {
OutputType result = processHelper(input);
for (Processor<OutputType, ?> nextProcessor : nextProcessors) {
nextProcessor.process(result);
}
}
}
private class RandomNumberGenerator extends BaseProcessor<Integer, Integer> {
@Override
Integer processHelper(Integer input) {
int generatedNumber = new Random().nextInt(input);
return generatedNumber;
}
}
private class IncrementProcessor extends BaseProcessor<Integer, Integer> {
@Override
Integer processHelper(Integer input) {
return input + 1;
}
}
private class DoubleProcessor extends BaseProcessor<Integer, Integer> {
@Override
Integer processHelper(Integer input) {
return input * 2;
}
}
private class Outputer extends BaseProcessor<Integer, String> {
String name;
public Outputer(String name) {
this.name = name;
}
@Override
String processHelper(Integer input) {
String output = String.format("Pipeline %s result: %d", name, input);
System.out.println(output);
return output;
}
}
public void buildAndRunPipeline() {
Processor<Integer, String> doublingOutputter = new Outputer("Doubling");
Processor<Integer, String> incrementingOutputter = new Outputer("Incrementing");
Processor<Integer, String> rngOutputter = new Outputer("Generating number");
Processor<Integer, Integer> doubler = new DoubleProcessor();
doubler.addOutputProcessor(doublingOutputter);
Processor<Integer, Integer> incrementer = new IncrementProcessor();
incrementer.addOutputProcessor(incrementingOutputter);
Processor<Integer, Integer> starter = new RandomNumberGenerator();
starter.addOutputProcessor(rngOutputter);
starter.addOutputProcessor(doubler);
starter.addOutputProcessor(incrementer);
starter.process(20);
}
public static void main(String[] args) {
Pipeline p = new Pipeline();
p.buildAndRunPipeline();
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
否
。因此,
*处理器[int,任何]
与*处理器[int,int]
的类型不同,并且您不能将一个分配给另一个类型您的错误消息,要构建一个长的链条,您需要参数
process
方法本身,但这在GO 1.18中不可能,即使您这样做,您也会在不知道下一个处理器的输出类型的同一问题中继续存在。
例如在Haskell中实现
。
我相信,您可以在没有反射的情况下获得的最接近, 类型
处理器
是多余的,但是将其更靠近您的代码):游乐场: https://go.dev/play/p/iv-virkatyb
No.
In Go
any
is just a static type (alias ofinterface{}
. It can never be a replacement for Java's unbounded wildcard?
. So*Processor[int, any]
is just not the same type as*Processor[int, int]
and you can't assign one to the other, as reported by your error message.In order to construct an arbitrarily long chain you would need to parametrize the
Process
method itself, but this is not possible in Go 1.18. You must declare all type parameters on the type itself. Though, even if you do this, you will keep incurring in the same issue of not knowing the output type of the next processor.Generally speaking, using a for loop can't work because the static types of the in/out values keep changing.
I believe the closest you can get without reflection is to implement some sort of composition operator — like
.
in haskell, via a top-level function. But you would have to manually nest calls.A simplified example (the type
Processor
is redundant, but keeping it closer to your code):Playground: https://go.dev/play/p/Iv-virKATyb
您不能使用任何关键字来实例化通用类型的值。
您实际上可以,但是第二个参数始终应为接口{}。但这不是您问题的答案的一部分。
要解决您的问题,您可以使用通用界面,而是
https://go.dev/play/p/b1wlovsbb0i
You can't use any keyword to instantiate the value of generic type.
You can actually, but the second parameter always should be interface{}. But it's not a part of answer to your question.
To solve your issue you can use generic interface instead
https://go.dev/play/p/B1wlOvSbb0I