Перебирать вложенные списки в Elixir - PullRequest
0 голосов
/ 11 ноября 2018

Я пытаюсь разобраться с этой проблемой довольно долго, и только найденные мной решения действительно ужасны. Должен быть более хороший способ решить эту проблему:

С учетом конфигурации

iterations = 2
tasklist = [
  {:system, print: "sth"},
  {:system, loop: [
    {:device1, run: "cmd" },
    {:device2, run: "cmd" },
  ], iterations: 3},
  {:device3, run: "cmd" },
]

Конфигурация обрабатывается сверху вниз, и каждая команда выводит некоторый результат (вывод в файл CSV), кроме {:system, loop: []}. Определения команд различны и могут включать диапазоны в качестве входных значений.

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

Итак, в основном это функция, которая преобразует исходную конфигурацию в такую:

tasklist = [
  {:system, print: "sth",mainiteration: 1, maintotalit: 2},
  {:device1, run: "cmd" ,loopiteration: 1, looptotalit: 3},
  {:device2, run: "cmd" ,loopiteration: 1, looptotalit: 3},
  {:device3, run: "cmd" ,mainiteration: 1, maintotalit: 2},

  {:system, print: "sth",mainiteration: 1, maintotalit: 2},
  {:device1, run: "cmd" ,loopiteration: 2, looptotalit: 3},
  {:device2, run: "cmd" ,loopiteration: 2, looptotalit: 3},
  {:device3, run: "cmd" ,mainiteration: 1, maintotalit: 2},

  {:system, print: "sth",mainiteration: 1, maintotalit: 2},
  {:device1, run: "cmd" ,loopiteration: 3, looptotalit: 3},
  {:device2, run: "cmd" ,loopiteration: 3, looptotalit: 3},
  {:device3, run: "cmd" ,mainiteration: 1, maintotalit: 2},

  {:system, print: "sth",mainiteration: 2, maintotalit: 2},
  {:device1, run: "cmd" ,loopiteration: 1, looptotalit: 3},
  {:device2, run: "cmd" ,loopiteration: 1, looptotalit: 3},
  {:device3, run: "cmd" ,mainiteration: 2, maintotalit: 2},

  {:system, print: "sth",mainiteration: 2, maintotalit: 2},
  {:device1, run: "cmd" ,loopiteration: 2, looptotalit: 3},
  {:device2, run: "cmd" ,loopiteration: 2, looptotalit: 3},
  {:device3, run: "cmd" ,mainiteration: 2, maintotalit: 2},

  {:system, print: "sth",mainiteration: 2, maintotalit: 2},
  {:device1, run: "cmd" ,loopiteration: 3, looptotalit: 3},
  {:device2, run: "cmd" ,loopiteration: 3, looptotalit: 3},
  {:device3, run: "cmd" ,mainiteration: 2, maintotalit: 2},
]

Текущий код, который проходит через шаги конфигурации:

Enum.each(1..iterations, fn n ->  
  Enum.each(tasklist, fn task ->
     IO.inspect(task) # My custom implementation
  end)
end)

Ответы [ 2 ]

0 голосов
/ 13 ноября 2018

Хорошо, поэтому первоначальная проблема была слишком сложной, чтобы решить ее таким образом ... Мы решили не включать опцию вложенных списков, как это было описано, и вместо этого интегрировать только один раздел инициализации поверх остальной части программного обеспечения. Итак, окончательный код выглядит так, просто для записи:

initit = 3
initlist = 
  [
    {:system, print: 10..15, header: "\tInitHeader1"},
  ]

mainit = 2
tasklist =
  [
    {:system, print: 1..5, header: "\tMainTaskHeader1"},
    {:system, print: 2..10, header: "\tMainTaskHeader2"}
  ]

Enum.each(1..initit, fn i ->
    Enum.each(1..mainit, fn t ->

      Enum.each(initlist, fn task ->
        runTask(task, initit, i)
      end)

      Enum.each(tasklist, fn task ->
        runTask(task, mainit, t)
      end)

    end)
  end)

Большое спасибо всем.

0 голосов
/ 12 ноября 2018

Добро пожаловать в переполнение стека!

Для начала, является неизменным, функциональным языком, поэтому распространенная концепция "петель", основанная на , с которой вы, возможно, знакомы, на самом деле здесь не применима. Но, конечно, вы все же можете перебирать коллекции данных другими способами, включая each/2, map/2, reduce/4 и понимания .

Если вы просто хотите напечатать результат каждого элемента или запустить одноразовое событие в итерации и ничего не возвращать, each/2 - самый простой вариант, в противном случае вам лучше использовать map/2 или reduce/3


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

defmodule TaskList do
  def handle({:system, print: message}) do
    IO.puts(message)
  end

  def handle({:system, loop: tasks, iterations: count}) do
    Enum.each(1..count, fn _ ->
      Enum.each(tasks, &handle/1)
    end)
  end

  def handle({device, run: command}) do
    # Call your command on the device
    # (Replace with your own implementation)
  end

  def handle(unknown) do
    # Do nothing or handle unexpected situations here
  end

  def process_all(tasks, iterations) when is_list(tasks) do
    handle({:system, loop: tasks, iterations: iterations})
  end
end

После внесения нескольких незначительных изменений в зависимости от того, как вы хотите получить вывод, вы можете затем назвать его так:

TaskList.process_all(my_tasks, 2)
...