英文:
I am writing a chat app that will have irc style commands. What is the best way to structure the program to process those commands?
问题
我正在编写一个基本上是聊天程序的东西。我想在其中包含一些特殊的IRC风格命令,但我似乎无法构思出如何组织程序的结构。我想知道是否有人可以从高层次的角度帮助我。我不需要代码,只是想要一些关于如何最好地进行的想法。
我最好的尝试是创建一个如下的Command结构体:
type Command struct {
name string // 命令的名称
function string // 匹配时将调用的函数
help string // 命令的帮助信息
regex string // 运行命令的正则表达式模式
}
然后创建一个Command的切片,并在每次接收到来自客户端的消息时迭代该切片。如果接收到的数据与正则表达式匹配,则使用反射(我认为这样会起作用)包调用"function"。出于某种原因,我觉得一定有更好的方法。我正在自学Go编程,没有资源可以与之交流想法。非常感谢您对此的想法。
英文:
I am writing what is essentially a chat program. I want to include some special irc style commands in it and I just can't seem to conceptualize how to structure the program. I'm wondering if people could help me out from a high level perspective. I'm not looking for code, just ideas on how to best proceed.
My best attempt is to have a Command struct like below:
type Command struct {
name string // the name of the command
function string // the function that will be called upon match
help string // the help message for the command
regex string // the regex pattern that will run the command
}
and then have a slice of Commands and just iterate over that every time I receive a message from the client. If the received data matches the regex then have the reflect (I think this will work) package call "function". For some reason, I feel like there has to be a better way. I'm learning to code Go on my own and don't have resources available to me to bounce ideas off of. I would very much appreciate your thoughts on this.
答案1
得分: 1
在IRC风格的命令中,命令行通常如下所示:
/cmd [param1] [param2] ... [paramn]
当接收到这样的命令时,你可以使用strings.Split()
函数将其拆分为命令的各个部分或标记。通过这种方式,你将获得第一个标记来识别命令。
你可以构建一个map[string]Command
映射,将文本命令映射到它们的Command
结构。在这个映射中,你可以通过简单地索引映射来获取命令,例如:
cmdMap := make(map[string]Command)
// 填充映射
textCmd := "/help"
cmd := cmdMap[textCmd]
如果你想要有命令别名(例如,你希望/help
、/h
和/?
都执行相同的操作),你可以为每个命令存储别名列表,并在构建cmdMap
时,为所有别名添加指向相同Command
结构的条目,此时你应该这样定义它:
cmdMap := make(map[string]*Command)
helpCmd := &Command{...} // 创建帮助命令
cmdMap["/help"] = helpCmd
cmdMap["/h"] = helpCmd
cmdMap["/?"] = helpCmd
注意:你也可以去掉前导斜杠'/'
,只使用命令的其余部分(在这种情况下为"help"
、"h"
和"?"
)来初始化你的映射,这取决于你自己。
此外,你不必存储函数的名称,因为在Go语言中,函数是值,所以你可以在Command
结构中拥有一个函数字段,然后可以直接调用该函数,而无需使用反射。例如:
func DoSomething() {
fmt.Println("Doing something...")
}
var someFv = DoSomething
// 现在你可以这样做:
someFv()
英文:
In IRC style commands usually a command line looks like this:
/cmd [param1] [param2] ... [paramn]
When such a command is received, you can split it using strings.Split()
to get the parts or tokens of the command. By this you will have the first token identifying the command.
You can build a map[string]Command
map where you map from text commands to their Command
structure. In this map you can get commands by simply indexing the map, e.g.:
cmdMap := make(map[string]Command)
// Populate map
textCmd := "/help"
cmd := cmdMap[textCmd]
If you want to have command aliases (e.g. you want /help
and /h
and /?
all to do the same), you can store the list of aliases for each command and when you build the cmdMap
, also add entries for all aliases to point to the same Command
structure, in which case you should define it like this:
cmdMap := make(map[string]*Command)
helpCmd := &Command{...} // Create help command
cmdMap["/help"] = helpCmd
cmdMap["/h"] = helpCmd
cmdMap["/?"] = helpCmd
Note: you could also strip off the leading slash '/'
and just use the rest of the command ("help"
, "h"
and "?"
in this case) to init your map, it's up to you.
Also you don't have to store the name of the function, functions in Go are values so you can have a function field in your Command
struct and then you can call that function without reflection. For example:
func DoSomething() {
fmt.Println("Doing something...")
}
var someFv = DoSomething
// And now you can do:
someFv()
See Function types and Function literals in the Go Language Specification.
答案2
得分: 0
首先,你不需要使用反射(reflect)。你可以在Command
结构体中包含一个函数类型的成员。
type Command struct {
name string // 命令的名称
f func(string) // 在匹配时将被调用的函数
help string // 命令的帮助信息
regex regexp.Regexp // 运行该命令的正则表达式模式
}
func processMessage(text string){
for _, cmd := range allCmds {
if cmd.regex.MatchString(text) {
cmd.f(text)
return
}
}
defaultAction(text) // 或者只需添加一个具有`.*`正则表达式的捕获所有命令
}
然后,你可以使用具有适当签名的函数添加命令:
cmd := Command{name: "foo", f: func(text string){fmt.Println(text)}}
它不一定要具有完全相同的签名。你可以让它接受一个连接或其他任何内容。你也不必内联函数定义,可以引用任何具有适当签名的函数。
英文:
First off, you don't need to use reflect. You can have the Command
struct contain a member with a func type.
type Command struct {
name string // the name of the command
f func(string) // the function that will be called upon match
help string // the help message for the command
regex regexp.Regexp // the regex pattern that will run the command
}
func processMessage(text string){
for _,cmd := range(allCmds){
if cmd.regex.MatchString(text){
cmd.f(text)
return
}
}
defaultAction(text) //or just add a catch-all with a regex of `.*`
}
Then you can add commands with a function of the appropriate signature:
cmd := Command{name: "foo",f: func(text string){fmt.Println(text)}}
it doesn't have to have exactly that signature. You can have it accept a connection or whatever. You also don't have to inline the function definition, you can reference any function you want that has the appropriate signature.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论