Functional programming 函数式:构造一个整数列表1..n

Functional programming 函数式:构造一个整数列表1..n,functional-programming,recursion,scheme,sml,Functional Programming,Recursion,Scheme,Sml,这不是家庭作业。我正在自学标准ML。我也知道一些计划,所以这个问题应该用两种语言来回答 我自己强加的任务是编写一个函数,构造一个从1到n的整数列表。例如,列表(7)应该返回[1,2,3,4,5,6,7]。O(n)溶液是理想的 很容易在线性时间内反向构造列表(即[n,n-1,…,1]): fun list 1 = 1::nil | list n = n::list(n-1); 我试图构建一个向前的列表是O(n^2),因为追加操作是线性的 fun list 1 = 1::nil | lis

这不是家庭作业。我正在自学标准ML。我也知道一些计划,所以这个问题应该用两种语言来回答

我自己强加的任务是编写一个函数,构造一个从1到n的整数列表。例如,列表(7)应该返回[1,2,3,4,5,6,7]。O(n)溶液是理想的

很容易在线性时间内反向构造列表(即[n,n-1,…,1]):

fun list 1 = 1::nil
|   list n = n::list(n-1);
我试图构建一个向前的列表是O(n^2),因为追加操作是线性的

fun list 1 = 1::nil
|   list n = list(n-1) @ n::nil;
我的下一个尝试是通过从nil开始,将n附加到前面,然后向后递归到1,从末尾到前面(从右到左)构建一个列表。但它根本不起作用

fun list n = (if n = 1
              then 1
              else list(n-1) :: n) :: nil;

有些事情让我觉得我需要一个helper函数来构建用于递归的未终止列表,但我被难住了。

一个经典方法是以相反的顺序构建它,然后将其反转。这是O(n)的两倍,当然也就是O(n)。

这里有一个解决方案:

fun list n =
  let
    fun f 1 m = m::nil
    |   f n m = m::f (n-1) (m+1)
  in
    f n 1
  end;

下面是一个使用辅助函数和尾部递归启用累加器的版本:

fun list n =
  let
    fun aux i acc = 
      if i > 0
      then aux (i-1) (i::acc)
      else acc
  in
    aux n nil
  end;
使用, 用一个简单的累加器, 这将构建一个从末尾开始的列表。如果你想一想减价

   list 5
=> list' nil 5
=> list' (5::nil) 4
=> list' (4::5::nil) 3
=> list' (3::4::5::nil) 2
=> list' (2::3::4::5::nil) 1
=> list' (1::2::3::4::5::nil) 0
=> [1, 2, 3, 4, 5]
   list 5
=> list' 1 (fn x => x)
=> list' 2 (fn x => (fn x => x) (1::x))
=> list' 3 (fn x => (fn x => (fn x => x) (1::x)) (2::x))
=> list' 4 (fn x => (fn x => (fn x => (fn x => x) (1::x)) (2::x)) (3::x))
=> list' 5 (fn x => (fn x => (fn x => (fn x => (fn x => x) (1::x)) (2::x)) (3::x)) (4::x))
=> list' 6 (fn x => (fn x => (fn x => (fn x => (fn x => (fn x => x) (1::x)) (2::x)) (3::x)) (4::x)) (5::x))
=> (fn x => (fn x => (fn x => (fn x => (fn x => (fn x => x) (1::x)) (2::x)) (3::x)) (4::x)) (5::x)) nil
=> (fn x => (fn x => (fn x => (fn x => (fn x => x) (1::x)) (2::x)) (3::x)) (4::x)) (5::nil)
=> (fn x => (fn x => (fn x => (fn x => x) (1::x)) (2::x)) (3::x)) (4::5::nil)
=> (fn x => (fn x => (fn x => x) (1::x)) (2::x)) (3::4::5::nil)
=> (fn x => (fn x => x) (1::x)) (2::3::4::5::nil)
=> (fn x => x) (1::2::3::4::5::nil)
=> [1, 2, 3, 4, 5]
或者, 有些事情让我觉得我需要一个helper函数来构建非终止列表,以便在递归中使用,但我被难倒了

未终止列表的表示是一个函数,它接受一个列表并返回一个列表:例如,要表示
10::\ufn
,可以使用
fn x=>10::x

fun list n =
    let fun list' m k = if m > n then k nil else
                        list' (m+1) (fn x => k (m::x))
    in list' 1 (fn x => x) end
再说一次,如果你想到减少

   list 5
=> list' nil 5
=> list' (5::nil) 4
=> list' (4::5::nil) 3
=> list' (3::4::5::nil) 2
=> list' (2::3::4::5::nil) 1
=> list' (1::2::3::4::5::nil) 0
=> [1, 2, 3, 4, 5]
   list 5
=> list' 1 (fn x => x)
=> list' 2 (fn x => (fn x => x) (1::x))
=> list' 3 (fn x => (fn x => (fn x => x) (1::x)) (2::x))
=> list' 4 (fn x => (fn x => (fn x => (fn x => x) (1::x)) (2::x)) (3::x))
=> list' 5 (fn x => (fn x => (fn x => (fn x => (fn x => x) (1::x)) (2::x)) (3::x)) (4::x))
=> list' 6 (fn x => (fn x => (fn x => (fn x => (fn x => (fn x => x) (1::x)) (2::x)) (3::x)) (4::x)) (5::x))
=> (fn x => (fn x => (fn x => (fn x => (fn x => (fn x => x) (1::x)) (2::x)) (3::x)) (4::x)) (5::x)) nil
=> (fn x => (fn x => (fn x => (fn x => (fn x => x) (1::x)) (2::x)) (3::x)) (4::x)) (5::nil)
=> (fn x => (fn x => (fn x => (fn x => x) (1::x)) (2::x)) (3::x)) (4::5::nil)
=> (fn x => (fn x => (fn x => x) (1::x)) (2::x)) (3::4::5::nil)
=> (fn x => (fn x => x) (1::x)) (2::3::4::5::nil)
=> (fn x => x) (1::2::3::4::5::nil)
=> [1, 2, 3, 4, 5]

在这种情况下,算法的结构可以使普通的数据结构足以满足累加器的要求,但使用连续性作为累加器是一种非常强大和有用的技术,不应忽视。

对于这样的列表问题,通常更容易解决更一般的问题


我如何建立一个包含整数的列表,使得n必须查找如何进行线性时间反转。我想你指的是m::f(n-1)(m+1)而不是m::list(n-1)(m+1)也许只是我,但每当我看到一个可以轻松重写为尾部递归的非尾部递归函数时,我都会退缩。我只是想展示基于Barry已有的最明显的解决方案。我想我应该注意到尾部递归解决方案是可能的,但Chris Conway已经做到了。这只是一个小小的畏缩;)如果这不是出于教育目的,我建议
funlistn=list.tablate(n,fnx=>x+1)
作为最佳实现。事实上,也许我仍然会这样做…结果会是相反的吗?这与我的第一个答案几乎相同,尽管我翻转了助手的参数顺序,所以部分计算结果很好。如果我早些时候意识到这一点,我可能不会写我的:)你会注意到这里的两个方法都是尾部递归的。这是累加器的另一个用途。想一想优化编译器如何使用累加器将非尾部递归函数转换为尾部递归函数,然后看看是否可以构造算法来实现这一目标。这正是我解决这个问题的目的。我想如果我能让它为1..n工作,我最终能让它为m..n工作。实际上,在ephemient的帮助下,我创建了一个函数,可以从第一个、第二个和最后一个元素创建列表,类似于Haskell允许用户构造列表的方法之一。您甚至可以利用部分求值并编写
val list=range 1
:)@ephemient:Sweet。(但我认为你的意思是部分应用。)使用SRFI-1的展开权:(定义(iota n)(展开权为零?1-1-n))(其中1-的定义是通常的方式:(定义(1-x)(-x1)))以相反的顺序构建,然后反向:(定义(iota n)(反向(展开权为零?1-1-n)))
   list 5
=> list' 1 (fn x => x)
=> list' 2 (fn x => (fn x => x) (1::x))
=> list' 3 (fn x => (fn x => (fn x => x) (1::x)) (2::x))
=> list' 4 (fn x => (fn x => (fn x => (fn x => x) (1::x)) (2::x)) (3::x))
=> list' 5 (fn x => (fn x => (fn x => (fn x => (fn x => x) (1::x)) (2::x)) (3::x)) (4::x))
=> list' 6 (fn x => (fn x => (fn x => (fn x => (fn x => (fn x => x) (1::x)) (2::x)) (3::x)) (4::x)) (5::x))
=> (fn x => (fn x => (fn x => (fn x => (fn x => (fn x => x) (1::x)) (2::x)) (3::x)) (4::x)) (5::x)) nil
=> (fn x => (fn x => (fn x => (fn x => (fn x => x) (1::x)) (2::x)) (3::x)) (4::x)) (5::nil)
=> (fn x => (fn x => (fn x => (fn x => x) (1::x)) (2::x)) (3::x)) (4::5::nil)
=> (fn x => (fn x => (fn x => x) (1::x)) (2::x)) (3::4::5::nil)
=> (fn x => (fn x => x) (1::x)) (2::3::4::5::nil)
=> (fn x => x) (1::2::3::4::5::nil)
=> [1, 2, 3, 4, 5]
fun range n m = if n > m then [] else n :: range (n+1) m
fun list n = range 1 n