2013-10-28 4 views
4

Я хочу содержать все мои команды на карте и карту от команды до функции, выполняющей задание (только стандартная таблица рассылки). Я начал со следующим кодом:Использование рекурсивных ссылок в Go

package main 

import "fmt" 

func hello() { 
    fmt.Print("Hello World!") 
} 

func list() { 
    for key, _ := range whatever { 
     fmt.Print(key) 
    } 
} 

var whatever = map[string](func()) { 
    "hello": hello, 
    "list": list, 
} 

Однако он не компилируется, поскольку существует рекурсивная ссылка между функцией и структурой. Пытаться переслать-объявить, что функция завершилась с ошибкой об повторном определении, когда она определена, и карта находится на верхнем уровне. Как вы определяете структуры, подобные этому, и инициализируете их на верхнем уровне без, используя функцию init().

Я не вижу хороших объяснений в определении языка.

  • Переходящая ссылка, которая существует, предназначена для «внешних» функций, и она не компилируется, когда я пытаюсь переслать-объявить функцию.
  • Я не нахожу способ переадресации переменной.

Обновление: Я ищу решение, которое не требует, чтобы вы заполнить переменную явно при запуске программы, ни в init() функции. Не уверен, что это вообще возможно, но работает на всех сопоставимых языках, о которых я знаю.

Обновление 2:FigmentEngine предложил подход, который я дал в качестве ответа ниже. Он может обрабатывать рекурсивные типы, а также допускать статическую инициализацию карты всех команд.

+0

Я понимаю ваше намерение и почему вы хотите, чтобы golang сделал это возможным. однако ИМХО это похоже на плохую практику, так как list() находится в контейнере, и он знает, в каком контейнере он находится, infact он будет работать только для этого конкретного контейнера - я думаю, вам нужна некоторая инверсия зависимости. все функции, когда они становятся нетривиальными, потребуют некоторых параметров для работы. Поэтому убивайте двух зайцев одним камнем и все функции требуют параметра контекста, это всегда может включать ссылку на контейнер и позволяет поддерживать повторное использование и несколько диспетчеров. – FigmentEngine

+0

Хм ... не думал об этом ... хорошая идея ... –

+0

Эх, это означало бы, что вам нужно определить рекурсивный тип; что это вообще возможно? Знаете, это не действительно μ-исчисление. –

ответ

1

Основываясь на предложении FigmentEngine выше, на самом деле можно создать статически инициализированный массив команд. Однако вы должны предварительно объявить тип, который вы передаете этим функциям. Я приведу переписанный пример ниже, поскольку он, вероятно, будет полезен другим.

Назовем новый тип Context. Он может содержать круговую ссылку, как показано ниже.

type Context struct { 
    commands map[string]func(Context) 
} 

Как только это будет сделано, можно объявить массив на высшем уровне, как это:

var context = Context { 
    commands: map[string]func(Context) { 
     "hello": hello, 
     "list": list, 
    }, 
} 

Заметим, что это совершенно нормально для обозначения функций, определенных ниже в файле, поэтому мы Теперь можно ввести функции:

func hello(ctx Context) { 
    fmt.Print("Hello World!") 
} 

func list(ctx Context) { 
    for key, _ := range ctx.commands { 
     fmt.Print(key) 
    } 
} 

с, что сделано, мы можем создать основную функцию, которая будет вызывать каждую из функций в заявленном контексте:

func main() { 
    for key, fn := range context.commands { 
     fmt.Printf("Calling %q\n", key) 
     fn(context) 
    } 
} 
0

Просто заполните карту внутри функции, прежде чем использовать list(). Мне нравится that.

Sry Я не видел, что вы написали «без init()»: это невозможно.

+0

Это просто альтернативный способ заполнения его явно, вместо того, чтобы поместить его в функцию '' init(). –

+0

Да, я только что отредактировал свой ответ :) –

2

Как вы уже обнаружили, что Go specifications государства (курсив мой):

если инициализатор A зависит от B, A будет установлен после анализа B. Dependency не зависит от фактических значений элементов, которые инициализируются, только по их появлению в источнике. A зависит от B, если значение A содержит упоминание B, содержит значение, инициализатор которого упоминает B, или упоминает функцию, которая рекурсивно упоминает B. Это ошибка, если такие зависимости образуют цикл.

Таким образом, нет, невозможно сделать то, что вы пытаетесь сделать. Issue 1817 упоминает эту проблему, и Русс Кокс говорит, что подход в Go может иногда быть чрезмерно ограничительным. Но это ясно и четко определено, и обходные пути доступны.

Таким образом, путь вокруг него по-прежнему осуществляется с помощью init(). Сожалею.

+0

Это подтвердило мои подозрения. Плохо, это то, что затрудняет решение подобных проблем. Надеюсь, они разрешат это в будущей версии. –

Смежные вопросы