如何在scala3中编写内联for循环?

问题描述 投票:0回答:3

我在scala中写过一些性能关键的代码,并且时不时地遇到如下代码:

var index = start - 1
while {index += 1; index < end} do something 

然后我想写一个内联版本以避免再次出现

var index
while { .. }

inline def forward(start: Int, end: Int)(inline body: Int => Unit): Unit =
    if start < end then
        body(start)
        forward(start + 1, end)(body)

我认为

forward(start, end){ something }
会生成与手写
while {}
几乎相同的代码。

但是我遇到了一条有线的编译消息让我很困惑:

object InlineFor:
    val n: Int = 1

    inline def forward(start: Int, end: Int)(inline body: Int => Unit): Unit =
        if start < end then
            body(start)
            forward(start + 1, end)(body)

    def main(args: Array[String]): Unit =
        forward(0, 1)(println)  // This is OK
        forward(0, n)(println)  // This is failed

超出了连续内联的最大数量(32),也许这是 由递归内联方法引起?您可以使用 -Xmax-inlines 来 改变限制。

代码有什么问题吗? 3x 非常多。

scala inline scala-3
3个回答
3
投票

错误消息说明了一切:您有一个内联递归方法。

这仅支持已知的迭代次数(在编译时)以及由编译器标志定义的最大次数

-Xmax-inlines

编译器无法在不知道代码中的

n
的情况下内联代码。

请注意,您的代码中根本不需要

inline
,只需用
@tailrec
注释您的方法即可强制它是尾递归,编译器将生成优化的代码


0
投票

如果将 n 的类型更改为

1
,则可以解决此问题:

object InlineFor:
    val n: 1 = 1

    inline def forward(start: Int, end: Int)(inline body: Int => Unit): Unit =
        if start < end then
            body(start)
            forward(start + 1, end)(body)

    def main(args: Array[String]): Unit =
        forward(0, n)(println)  // ok again

原因是编译器需要知道类型级别

start
end 的值才能正确确定
if start < end

但是:我不认为内联递归是您正在寻找的。如果您调用

forward(0, 32)(println)
,您将再次遇到相同的编译器错误,因为这样您就真的达到了递归内联的默认限制(即 32)。

您应该意识到(大大简化)对内联函数的每次调用都将导致该调用被函数体替换。因此,生成的字节码将包含

body
字节码的 n 个实例,而不是循环。 (这就是递归内联调用受到限制的原因。)

如果您使用 Intellij IDEA:“查看”菜单下有一个“显示字节码”选项。我建议您使用它,并观察如果您增加

n
的值,您的字节码将如何增长。


0
投票

不要使用递归,因为你无法阻止递归调用被内联。将

while
var
一起使用 - 这个
var
将是本地的,隐藏在您的
InlineFor
对象中。这被认为是可以接受的,这就是实现了多少标准库函数。

inline def forward(start: Int, end: Int)(inline body: Int => Unit): Unit =
  var i = start
  while i < end do
    body(i)
    i = i + 1
© www.soinside.com 2019 - 2024. All rights reserved.