【问题标题】:How to check if an array is in sequential and consecutive order in elixir如何在长生不老药中检查数组是否按顺序和连续顺序
【发布时间】:2022-01-09 02:27:43
【问题描述】:

我想知道是否有办法找出一个数组是否按顺序排列和连续。例如

arr=[1,2,3,4,5]

Arr 会返回 true,因为它是连续的和连续的

arr2=[1,2,3,4,56]

这将返回 false,因为 56 不会在 4 之后立即出现。我尝试了很多方法,但很难,因为没有循环

【问题讨论】:

标签: elixir


【解决方案1】:

当助手不像普通的旧的良好裸递归那样简单时就是这种情况。此外,这是一项很棒的练习,因为如果没有清楚地了解如何处理递归带来的问题,就无法做到

defmodule Checkers do
  def seq?(input), do: do_seq?(input, nil)

  defp do_seq?([], _), do: true # we are done
  defp do_seq?([h | t], nil),
    do: do_seq?(t, h)           # entry
  defp do_seq?([h | t], ah) when ah == h - 1,
    do: do_seq?(t, h)           # success path
  defp do_seq?(_, _), do: false # ouch! we failed
end

Checkers.seq?([1, 2, 3, 4])
#⇒ true
Checkers.seq?([1, 3, 3, 4])
#⇒ false

绝对正确的Enum.reduce_while/3,由@peaceful-james 建议基本上完全符合under the hood。这个解决方案更简洁的版本是

Enum.reduce_while(arr, true, fn
  curr, prev when prev == true or curr == prev + 1 ->
    {:cont, curr}
  _curr, _prev ->
    {:halt, false}
end) # returns truthy value if seq

【讨论】:

    【解决方案2】:

    只为记录添加一个,这与 Aleksei 的递归相当接近,但没有辅助函数和额外的参数:

      def seq?([]), do: true
      def seq?([_]), do: true
      def seq?([i1, i2 | rest]) when i2 - i1 == 1, do: seq?([i2 | rest])
      def seq?([_ | _]) , do: false
    

    【讨论】:

    • 喜欢。最后一个子句实际上不需要非空列表匹配def seq?(_), do: false
    • 确实,我只是把它作为一种类型检查的形式,让它在非列表上引发FunctionClauseError,但逻辑上没有必要。
    【解决方案3】:
    [1,2,3,4,56] 
    |> Enum.chunk_every(2, 1, :discard) 
    |> Enum.all?(fn [x,y] -> y == x + 1 end)
    

    应该使用几个标准库函数来满足您的要求。

    chunk_every 为您提供一个滑动窗口,将您的列表转换为[[1, 2], [2, 3], [3, 4], [4, 56]],为下面的下一次检查提供顺序基础。 丢弃部分是删除最后的单个元素,即[1,2,3] |> Enum.chunk_every(2, 1) == [[1, 2], [2, 3], [3]]:discard 选项将省略最后一个(单个)项目(请参阅Enum.chunk_every/3 的文档。)

    然后使用Enum.all? 和给定的过滤函数检查每一对是否连续。

    【讨论】:

    • 这会产生一个多余的中间列表,这可能会在巨大的列表中消耗内存。
    • 最佳答案 IMO。将Enum.chunk_every 替换为Stream.chunk_every 以避免中间列表。
    • 我用包含 1 到 100,000 的列表对所有 4 种解决方案(普通递归、Stream.chunk_everyEnum.chunk_everyEnum.reduce_while)进行了基准测试。普通递归版本大约需要 670 微秒,这是最快的。 reduce_while 版本是第二个版本,大约 2550 微秒。其他 2 个与大约 22,000 微秒相当,这比普通递归要慢得多。
    【解决方案4】:

    您可以使用Enum.reduce,其初始值为表示{"is the list still consecutive and sequential?", "the last element processed by reduce"} 的元组{true, nil}

    arr = [1, 2, 3, 4, 5]
    
    {is_consecutive_and_sequential, _last_element} =
      Enum.reduce(arr, {true, nil}, fn
        current, {true, nil} when is_integer(current) -> {true, current}
        current, {true, previous} when is_integer(current) -> {current == previous + 1, current}
        current, {_anything, _previous} -> {false, current}
      end)
    

    如果您的列表很长,您可以使用Enum.reduce_while 以获得更有效的解决方案:

    {is_consecutive_and_sequential, _last_element} =
      Enum.reduce_while(arr, {true, nil}, fn
        current, {true, nil} when is_integer(current) ->
          {:cont, {true, current}}
    
        current, {true, previous} when is_integer(current) ->
          if current == previous + 1,
            do: {:cont, {true, current}},
            else: {:halt, {false, current}}
    
        current, {_anything, _previous} ->
          {:halt, {false, current}}
      end)
    

    【讨论】:

    • 除了previous,你实际上不需要在累加器中保留任何东西;一旦序列中断,您手头上已经有 :halt 返回错误值。
    【解决方案5】:

    更新:谦虚地包含了 Aleksei 的更正

    这是使用Enum.with_index的另一种方式

        myList = [2,3,4,5,56]
        [head | _] = myList                         # gets first element in the 'array'
        res= Enum.with_index( myList, head) |>      # so [{2,2},{3,3},{4,4},{5,5},{56,6}]
             Enum.all?(&match?({x,x}, &1))
       if res == 1 do
           "sequential"
        else
           "not sequential"
        end
    

    【讨论】:

    • myList |> Enum.with_index(head) |> Enum.all?(&match?({x,x}, &1))
    • 这不是一个改进,它是一个正确的解决方案,不像你的那样返回"sequential" for myList = [1,2,4,3] :)
    【解决方案6】:

    使用标准函数(虽然可能更昂贵)

    defp consecutive?(list) do
      [h | tl]= d = Enum.sort(list)
      d == Enum.to_list(h..List.last(tl))
    end
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-08-02
      • 1970-01-01
      • 1970-01-01
      • 2013-04-27
      • 2012-04-21
      • 2016-09-05
      • 2016-02-01
      • 2021-08-16
      相关资源
      最近更新 更多