我在这个问题上看到了这个引用:什么是构建Web服务的好函数语言?
特别是Scala不支持尾调用消除,除了自递归函数,这限制了你可以做的组合种类(这是JVM的一个基本限制).
这是真的?如果是这样,那么创建这个基本限制的JVM是什么呢?
注释(用户SOC上的)回答到关于尾调用优化的问题提到了Java 7中有一个称呼,是因为"加ARM的"的"抑制异常",新的功能(适用于ARM CPU的支持?).
在这种情况下,什么是"抑制异常"?在其他情况下,"被抑制的异常"将是一个被捕获然后被忽略的异常(很少是一个好主意); 这显然是不同的.
我遇到的一个编程问题涉及计算大数(最多10 ^ 5的数字)的阶乘.我见过一个简单的Haskell代码,就像这样
factorial :: (Eq x, Num x) => x -> x
factorial 0 = 1
factorial a = a * factorial (a - 1)
Run Code Online (Sandbox Code Playgroud)
即使没有代码中涉及的任何缓存,它也会隐式处理大量数字并以某种方式运行得更快.
当我尝试使用Java解决问题时,我不得不使用BigInteger来保存大数字并使用因子的迭代版本
public static BigInteger factorialIterative(int n)
{
if(n == 0 || n == 1) return BigInteger.valueOf(1);
BigInteger f = BigInteger.valueOf(1);
for(int i = 1 ; i <= n ;i++)
f = f.multiply(BigInteger.valueOf(i));
return f;
}
Run Code Online (Sandbox Code Playgroud)
上述代码超出了程序执行的设定时间限制.我也尝试了factorial的缓存递归版本
public static BigInteger factorial(int n)
{
if(cache[n] != null)
return cache[n];
else if(n == 0)
return new …Run Code Online (Sandbox Code Playgroud) 主要问题:我将尾部调用优化(TCO)的最重要应用视为递归调用到循环的转换(在递归调用具有某种形式的情况下).更准确地说,当翻译成机器语言时,这通常会转换成某种跳跃系列.编译为本机代码(例如SBCL)的一些Common Lisp和Scheme编译器可以识别尾递归代码并执行此转换.基于JVM的Lisp(例如Clojure和ABCL)在执行此操作时遇到了麻烦.JVM作为一台可以防止或使其变得困难的机器是什么?我不明白.JVM显然没有循环问题.编译器必须弄清楚如何进行TCO,而不是编译它的机器.
相关问题:Clojure 可以将看似递归的代码转换成循环:如果程序员用关键字替换对函数的尾调用,它就好像它正在执行TCO一样recur.但是,如果有可能让编译器识别尾调用 - 例如SBCL和CCL那样做 - 那么为什么Clojure编译器不能确定它应该按照它处理的方式处理尾调用recur呢?
(对不起 - 这无疑是一个常见问题解答,我确信上面的评论显示了我的无知,但我找不到早期的问题是不成功的.)
可能重复:
为什么JVM仍然不支持尾调用优化?
我在网上看到了很多不同的答案,所以我想我会问专家.
假设我有一个尾递归的递归函数.
System.out.println( sum(Arrays.asList(0, 1, 2, 3, 4, 5)) );
int sum(List<Integer> integers) {
if (integers.isEmpty())
return 0;
else
return integers.get(0) + sum(integers.subList(1, integers.size()));
}
Run Code Online (Sandbox Code Playgroud)
我想知道这个函数sum是否会在堆栈上增长,还是会被改为循环(因为它是一个尾递归函数)?
我刚刚读到Scala检测到这样的调用并对其进行优化但是这只是一个Scala专用的东西还是JVM?
我一直在阅读文章,描述如何通过使用尾递归版本来减少快速排序的空间复杂性,但我无法理解这是怎么回事.以下是两个版本:
QUICKSORT(A, p, r)
q = PARTITION(A, p, r)
QUICKSORT(A, p, q-1)
QUICKSORT(A, q+1, r)
TAIL-RECURSIVE-QUICKSORT(A, p, r)
while p < r
q = PARTITION(A, p, r)
TAIL-RECURSIVE-QUICKSORT(A, p, q-1)
p = q+1
Run Code Online (Sandbox Code Playgroud)
(来源 - http://mypathtothe4.blogspot.com/2013/02/lesson-2-variations-on-quicksort-tail.html)
据我所知,这两个都会导致数组的左半部分和右半部分的递归调用.在这两种情况下,一次只处理一半,因此在任何时候只有一个递归调用将使用堆栈空间.我无法看到尾递归快速排序如何节省空间.
上面的伪代码取自文章 - http://mypathtothe4.blogspot.com/2013/02/lesson-2-variations-on-quicksort-tail.html文章中 提供的解释让我更加困惑 -
Quicksort对给定的子数组进行分区并继续递归两次; 一个在左子阵列上,一个在右侧.这些递归调用中的每一个都需要其自己的堆栈空间流.此空间用于在某种递归级别存储数组的索引变量.如果我们想象这是从执行的开始到结束发生的,我们可以看到堆栈空间在每一层加倍.
那么Tail-Recursive-Quicksort如何修复所有这些呢?
好吧,我们现在只对一个子阵列进行递归,而不是在两个子阵列上递归.这消除了在每个执行层加倍堆栈空间的需要.我们通过使用while循环作为执行相同任务的迭代控件来解决这个问题.我们只需更改同一组变量并对新变量使用单个递归调用,而不是需要堆栈为两个递归调用保存变量集.
在常规快速排序的情况下,我没有看到堆栈空间在每个执行层都是如何加倍的.
注意: - 文章中没有提到编译器优化.
据我所知,由于函数调用的开销,递归函数通常效率低于等效的非递归函数.但是,我最近遇到了一本教科书,说Java(和C#)并不是必需的.
它没有说明原因,但我认为这可能是因为Java编译器以某种方式优化了递归函数.
有谁知道为什么会这样的细节?
我目前正在用Java编写快速排序算法来对随机的整数数组进行排序,然后使用System.nanoTime()对它们进行计时.这些数组的大小是10的幂,从10 ^ 3开始到10 ^ 7结束.此外,随机列表具有不同的属性.我正在整理纯粹的随机列表,列表中包含一些相同的值(很少),反向排序列表,排序列表和几乎排序的列表.
排序有效.它在数组上递归执行快速排序,直到它需要排序30个元素或更少的数组,在这种情况下,它执行插入排序.
一切都很好10 ^ 3和10 ^ 4但是一旦我达到10 ^ 5的值,它只会对随机,几个独特和随机列表进行排序,但在排序几乎排序和排序的列表时会产生堆栈溢出错误.
我不相信问题在于生成列表的方式,因为堆栈溢出发生在排序算法中(编译器引用的行是findPivot()方法中的第一行).此外,它通常会在崩溃之前对1到6个列表进行排序.因此,必须以某种方式使算法本身与几乎排序和排序的列表进行交互.此外,反向列表的生成涉及调用用于创建排序列表的代码(然后将其反转).
另外,我觉得不太可能,这个问题仅仅是不必,因为某些原因,通过递归在近分类和排序列表显著多次调用该分区关闭阵列的部分,而不是其他列表类型的算法,如它可以对10 ^ 7值的随机列表进行排序,这将需要比具有10 ^ 5值的近似排序列表更多的分区.
我意识到它必须与这些列表类型如何与我的快速排序的递归相互作用有关,但如果有人可以阐明它会很棒.我把代码放到了完整的快速排序算法和下面的随机列表生成器中.
快速排序算法
/**
* Performs a quick sort given the indexes of the bounds of an integer array
* @param arr The array to be sorted
* @param highE The index of the upper element
* @param lowE The index of the lower element
*/
public static void quickSort(int[] arr, int highE, int lowE)
{
//Only perform an action if arr.length …Run Code Online (Sandbox Code Playgroud) 我想解析谷歌附近的地方响应,一个项目有这种格式:
"geometry" : {
"location" : {
"lat" : 75.22404,
"lng" : 57.42276
},
"viewport" : {
"northeast" : {
"lat" : 95.2353532,
"lng" : 75.4427513
},
"southwest" : {
"lat" : 55.207256,
"lng" : 45.4045009
}
}
},
"vicinity" : "something"
Run Code Online (Sandbox Code Playgroud)
但是我想用一个像这样的对象来解析它:
public class NearbyPlace extends BaseResponse {
@JsonProperty("how to access geometry->lat ?")
private double latitude;
@JsonProperty("how to access geometry->lng ?")
private double longitude;
@JsonProperty("vicinity")
private String vicinity;
}
Run Code Online (Sandbox Code Playgroud)
问题是如何直接从NearbyPlace类访问"几何"中的"lat"和"lng"而不为每个节点创建另一个类?