首页 \ 问答 \ 优化或删除Elixir中reduce_while调用的用法(Optimize or remove the usage of a reduce_while call in Elixir)

优化或删除Elixir中reduce_while调用的用法(Optimize or remove the usage of a reduce_while call in Elixir)

我正在关注编译器上最近发布的Destroy All Software截屏视频。 所有示例代码都是用Ruby编写的,但我试图用Elixir实现相同的输出。

用简单的英语,我的方法是这样的:

  1. 将一串代码传递给递归函数( call_tokenizer
  2. 循环遍历令牌类型,并使用当前令牌类型运行正则表达式
  3. 如果找到匹配项,则将结果附加到累加器,从字符串的开头删除结果,并将剩余的字符串和累加器传递给call_tokenizer 。 另外, :halt `reduce_while循环,并返回累积的标记。
  4. 如果没有找到结果,继续循环

我在Elixir中编写了一些函数,它们的工作方式是我从输入中获得预期的输出。 但是,我想我的代码至少有一个问题。

我正在使用Enum.reduce_while ,但我不认为我正在使用它,因为它打算使用它。 我的假设是有一些更好的方法可以将reduce_while为递归函数。

如果我需要在一个问题中总结一下这将是如何实现相同的结果,而不依赖于reduce_while给我的halting / continue属性? 我应该注意这个代码示例是否还有其他问题?

这是我的代码和预期输出:

// Code
defmodule Compiler.Token do
  defstruct [:type, :value]
end

defmodule Compiler.Tokenizer do
  @token_types [
    {:def, "\\bdef\\b"},
    {:end, "\\bend\\b"},
    {:identifier, "\\b[a-zA-Z]+\\b"},
    {:integer, "\\b[0-9]+\\b"},
    {:oparen, "\\("},
    {:cparen, "\\)"}
  ]

  def tokenize() do
    code = "def f()
      1
    end"

    IO.inspect call_tokenize(code, [])
  end

  def call_tokenize("", accumulator) do
    accumulator
  end

  def call_tokenize(code, accumulator) do
    Enum.reduce_while(@token_types, "", fn {type, re}, acc ->
      result = Regex.run(~r/\A#{re}/, code)

      if result do
        value = hd(result)
        base = byte_size(value)
        token = %Compiler.Token{type: type, value: value}
        tokens = binary_part(code, base, byte_size(code) - base)
          |> String.trim()
          |> call_tokenize(accumulator ++ [token])
        {:halt, tokens}
      else
        {:cont, acc}
      end
    end)
  end
end


// Expected output
[%Compiler.Token{type: :def, value: "def"},
 %Compiler.Token{type: :identifier, value: "f"},
 %Compiler.Token{type: :oparen, value: "("},
 %Compiler.Token{type: :cparen, value: ")"},
 %Compiler.Token{type: :integer, value: "1"},
 %Compiler.Token{type: :end, value: "end"}]    

I'm following along with a recent Destroy All Software screencast on Compilers. All of the example code is written in Ruby, but I'm trying to achieve the same outputs with Elixir.

In plain-english, my approach goes something like this:

  1. pass a string of code to a recursive function (call_tokenizer)
  2. loop over token-types, and run a regex with the current token-type
  3. if a match is found, append that result to an accumulator remove the result from the beginning of the string, and pass the remaining string and the accumulator to call_tokenizer. Additionally, :halt the `reduce_while loop, and return the accumulated tokens.
  4. if no result was found, continue on with the loop

I've written a couple functions in Elixir that work in the sense that I get the expected outputs from my input. However, I think I already have at least one issue with my code.

I'm using Enum.reduce_while, but I don't think I'm using it as it's intended to be used. My assumption is that there's some better way to re-write the reduce_while as a recursive function.

If I needed to summarize that in a question it would be how do I achieve the same result, without relying on the halting/continue properties that reduce_while give me? Are there any other issues with this code sample that I should be aware of?

Here is my code and expected output:

// Code
defmodule Compiler.Token do
  defstruct [:type, :value]
end

defmodule Compiler.Tokenizer do
  @token_types [
    {:def, "\\bdef\\b"},
    {:end, "\\bend\\b"},
    {:identifier, "\\b[a-zA-Z]+\\b"},
    {:integer, "\\b[0-9]+\\b"},
    {:oparen, "\\("},
    {:cparen, "\\)"}
  ]

  def tokenize() do
    code = "def f()
      1
    end"

    IO.inspect call_tokenize(code, [])
  end

  def call_tokenize("", accumulator) do
    accumulator
  end

  def call_tokenize(code, accumulator) do
    Enum.reduce_while(@token_types, "", fn {type, re}, acc ->
      result = Regex.run(~r/\A#{re}/, code)

      if result do
        value = hd(result)
        base = byte_size(value)
        token = %Compiler.Token{type: type, value: value}
        tokens = binary_part(code, base, byte_size(code) - base)
          |> String.trim()
          |> call_tokenize(accumulator ++ [token])
        {:halt, tokens}
      else
        {:cont, acc}
      end
    end)
  end
end


// Expected output
[%Compiler.Token{type: :def, value: "def"},
 %Compiler.Token{type: :identifier, value: "f"},
 %Compiler.Token{type: :oparen, value: "("},
 %Compiler.Token{type: :cparen, value: ")"},
 %Compiler.Token{type: :integer, value: "1"},
 %Compiler.Token{type: :end, value: "end"}]    

原文:https://stackoverflow.com/questions/45667082
更新时间:2022-07-03 22:07

最满意答案

您无法以这种方式访问​​地图内的结构,因为可能没有结构。 在你的例子中, VStructs["foo"]["bar"].Field1会是什么VStructs["foo"]["bar"].Field1是什么? 您无法知道它是否真的是地图中的空结构,或者没有任何内容,并且地图访问只返回了该类型的空值。 你应该做的是一个明确的检查:

v, ok := VStructs["01"]["Struct1"]
if !ok {
    // Handle the case of the missing struct.
}
// It is also a good practice to check type assertions.
s, ok := v.(V01.Struct1)
if !ok {
    // Handle the case when v is not V01.Struct1.
}
f = s.Field1

Okay, since the question was probably in conflict with the way golang is used to work.

I have written an package to serve my needs :

Based on the selected version (In the URL or the Header, whatever you like) we retrieve an map of structs. We now can select the actual struct we need, set some values and finally send it back to the screen OR send it over to the DB (In my case gorp).

The Package can be found here https://github.com/donseba/contractor/

相关问答

更多
  • std::map<>::operator[]功能等同于 (*((std::map<>::insert(std::make_pair(x, T()))).first)).second 表达式,如语言规范中所述。 正如你所看到的,这涉及到默认构造一个类型为T的临时对象,将它复制到一个std::pair对象中,然后将它复制到地图的新元素中(假设它已经不存在了)。 显然,这会产生一些中间T对象。 破坏这些中间物体是你在实验中观察到的。 你错过了他们的建设,因为你不会从你的班级的复制构造者那里得到任何反馈。 中间对 ...
  • 您可以使用reflect.DeepEqual ,或者您可以实现自己的功能(性能明智会比使用反射更好): http://play.golang.org/p/CPdfsYGNy_ m1 := map[string]int{ "a":1, "b":2, } m2 := map[string]int{ "a":1, "b":2, } fmt.Println(reflect.DeepEqual(m1, m2)) You can use reflect.DeepEqual, ...
  • 您无法以这种方式访问地图内的结构,因为可能没有结构。 在你的例子中, VStructs["foo"]["bar"].Field1会是什么VStructs["foo"]["bar"].Field1是什么? 您无法知道它是否真的是地图中的空结构,或者没有任何内容,并且地图访问只返回了该类型的空值。 你应该做的是一个明确的检查: v, ok := VStructs["01"]["Struct1"] if !ok { // Handle the case of the missing struct. } / ...
  • 您的operator<()定义不明确。 假设我有a=Point{0,1}和b=Point{1,1} 。 那么ax < other.x) || ((this->x == other.x) && (this->y < ...
  • 每个进程都有自己的虚拟内存。 一个进程无法访问另一个进程的内存(除了某些特定于平台的方式,但这些情况不适用于动态内存)。 std::vector默认使用std::allocator分配其内部数组。 std::allocator分配动态内存。 将向量写入文件时,该向量将引用写入向量的进程的动态内存。 如果您尝试在另一个进程中读取该向量,则该进程尚未在原始进程所具有的虚拟内存位置中分配任何动态内存(除非纯粹的机会)。 因此,使用这种向量具有未定义的行为。 状态存储在动态内存中的对象不能在进程之间共享。 要在进程 ...
  • std::greater<>调用operator>()来完成它的工作,所以如果你想使用std::greater<> ,你需要重载。 它应该是这样的: inline bool operator>(const somecomplexstruct& lhs, const somecomplexstruct& rhs) { // implement your ordering here. } std::greater<> invokes operator>() to do its work, so you ...
  • 问题是你的m和l的内容不是conversionGroup而是conversionGroup的列表。 尝试这个: conversionGroups := make(map[string][]conversionGroup) 它应该解析。 注意conversionGroup之前的[] 。 那么问题是这是否是你真正想要的结构:) The problem is that the contents of your m and l aren't conversionGroups but lists of conver ...
  • 我认为答案应该取决于你的需求 ,创建对象的方式都可以。 有时你不需要担心malloc分配内存,如果你只想使用一个对象几次,并且当函数退出时你不需要对象“幸存”。 所以你的变种A就可以了。 有时您想要创建大量对象并将它们存储在其他数据结构(列表,树等)中,并且您需要对象在整个程序中“生存”。 在这种情况下,您的变体B会更好。 AnT的答案向您展示了如何为初始化保存代码行。 另一次,当我考虑使用malloc时,我知道结构非常大并且会为每个对象消耗大量字节,因此拥有大量字节可能会占用所有堆栈。 在这种情况下,我宁 ...
  • 我们需要了解每次调用时内存分配是如何工作的: u := User{Name: "Leto"} // u is an object of type User // after this line memory has been allocated to both the // properties u.Name(string) and u.Map(reference) // lets say allocated memory address for u.Name starts with x // for ...
  • 问题是在O_Recognition.h extern块中未定义O_Recognition.h 。 它以前的定义仅适用于globals.h extern块。 只需要包含它的类型,以便Cython知道它是什么。 它不需要重新定义。 cdef extern from "globals.h" nogil: ctypedef struct circleData: int x; int y; int radius; cdef extern from "O_Rec ...

相关文章

更多

最新问答

更多
  • 您如何使用git diff文件,并将其应用于同一存储库的副本的本地分支?(How do you take a git diff file, and apply it to a local branch that is a copy of the same repository?)
  • 将长浮点值剪切为2个小数点并复制到字符数组(Cut Long Float Value to 2 decimal points and copy to Character Array)
  • OctoberCMS侧边栏不呈现(OctoberCMS Sidebar not rendering)
  • 页面加载后对象是否有资格进行垃圾回收?(Are objects eligible for garbage collection after the page loads?)
  • codeigniter中的语言不能按预期工作(language in codeigniter doesn' t work as expected)
  • 在计算机拍照在哪里进入
  • 使用cin.get()从c ++中的输入流中丢弃不需要的字符(Using cin.get() to discard unwanted characters from the input stream in c++)
  • No for循环将在for循环中运行。(No for loop will run inside for loop. Testing for primes)
  • 单页应用程序:页面重新加载(Single Page Application: page reload)
  • 在循环中选择具有相似模式的列名称(Selecting Column Name With Similar Pattern in a Loop)
  • System.StackOverflow错误(System.StackOverflow error)
  • KnockoutJS未在嵌套模板上应用beforeRemove和afterAdd(KnockoutJS not applying beforeRemove and afterAdd on nested templates)
  • 散列包括方法和/或嵌套属性(Hash include methods and/or nested attributes)
  • android - 如何避免使用Samsung RFS文件系统延迟/冻结?(android - how to avoid lag/freezes with Samsung RFS filesystem?)
  • TensorFlow:基于索引列表创建新张量(TensorFlow: Create a new tensor based on list of indices)
  • 企业安全培训的各项内容
  • 错误:RPC失败;(error: RPC failed; curl transfer closed with outstanding read data remaining)
  • C#类名中允许哪些字符?(What characters are allowed in C# class name?)
  • NumPy:将int64值存储在np.array中并使用dtype float64并将其转换回整数是否安全?(NumPy: Is it safe to store an int64 value in an np.array with dtype float64 and later convert it back to integer?)
  • 注销后如何隐藏导航portlet?(How to hide navigation portlet after logout?)
  • 将多个行和可变行移动到列(moving multiple and variable rows to columns)
  • 提交表单时忽略基础href,而不使用Javascript(ignore base href when submitting form, without using Javascript)
  • 对setOnInfoWindowClickListener的意图(Intent on setOnInfoWindowClickListener)
  • Angular $资源不会改变方法(Angular $resource doesn't change method)
  • 在Angular 5中不是一个函数(is not a function in Angular 5)
  • 如何配置Composite C1以将.m和桌面作为同一站点提供服务(How to configure Composite C1 to serve .m and desktop as the same site)
  • 不适用:悬停在悬停时:在元素之前[复制](Don't apply :hover when hovering on :before element [duplicate])
  • 常见的python rpc和cli接口(Common python rpc and cli interface)
  • Mysql DB单个字段匹配多个其他字段(Mysql DB single field matching to multiple other fields)
  • 产品页面上的Magento Up出售对齐问题(Magento Up sell alignment issue on the products page)