递归读取数组

时间:2017-06-07 12:38:50

标签: c arrays recursion

我正在学习如何将递归应用于数组。

例如,我通常以这种方式读取数组:

void read_array(int *a, int n){
        int i;
        for(i = 0; i < n; ++i)
                scanf("%d", &a[i]);
        return;
}

我想以递归方式读取数组。我写了以下函数:

void read_array(int *a, int n){
        int i = n - 1;
        if (n < 0)
                return;
        else{
                if(scanf("%d", &a[n - 1 - i]) == 1){
                        read_array(a, n - 1);
                        return;
                }
        }
}

它编译,但在运行时会产生分段错误错误。它使我感到困惑,因为该函数考虑了一个应该阻止它的基础案例0

5 个答案:

答案 0 :(得分:7)

您对数组索引的计算是错误的。这一行:

            if(scanf("%d", &a[n - 1 - i]) == 1){

假设{em>初始值为n,但与此同时,每次递归步骤减少 n。话虽如此,它不应该崩溃但只是反复写出a的第一个元素,因为i = n - 1 n - 1 - i总是为零。

编写这种递归的惯用方法是递归i

void read_array(int *a, int n, int i)
{
    if (i < n)
    {
        if(scanf("%d", &a[i]) == 1)
        {
            read_array(a, n, i+1);
        }
    }
}

并使用i的初始值调用它,例如read_array(a, 10, 0)用于读取10个元素的数组。

在实践中,应避免使用C语言中的递归。 *

*函数式语言通常可以优化递归,C只是使用带有大量开销的调用堆栈。

在这个例子中,写一个 pure 函数的递归的理论目的在一定程度上被一个返回void的函数所击败。如果这只是学习原理,那么函数实际上应该返回一些东西。例如,您可以创建一个功能“列表构建器”:

#include <stdio.h>
#include <stdlib.h>

// place the side effect in a separate function
int getValue(void)
{
    // could have `scanf()` here:
    return rand();
}

typedef struct List
{
    int a[10];
    size_t length;
} List;

// non-functional helper to get around limitations of C:
// (if it could initialize result directly with the new values, it would
// be functional)
List listAppend(List list, int val)
{
    List result = list;
    result.a[result.length++] = val;
    return result;
}

// recursive function without side effects:
List buildList(List list, int (*value)())
{
    if (list.length >= 10) return list;
    return buildList(listAppend(list, value()), value);
}

int main(void)
{
    List myList = buildList((List){0}, &getValue);

    for (size_t i = 0; i < myList.length; ++i)
    {
        printf("myList.a[%zu] is %d\n", i, myList.a[i]);
    }
}

答案 1 :(得分:3)

该功能存在错误。

由于变量i按以下方式初始化

int i = n - 1;

然后是此调用中的第二个参数

scanf("%d", &a[n - 1 - i])

评估为

scanf("%d", &a[n - 1 - (n - 1)])

它总是等于零

scanf("%d", &a[0])

由于使用指针a的相同值调用递归函数,因此所有输入的值都将分配给a[0]。数组的所有其他元素仍然未初始化。

虽然这不是该功能异常执行的原因。

有可能是使用了一个大数组而且堆栈太小而无法递归调用该函数。

在任何情况下,都可以通过以下方式更简单,更正确地定义函数

size_t read_array( int *a, size_t n )
{
    return n && scanf( "%d", a ) == 1 ? 1 + read_array( a + 1, n - 1 ) : 0;
}   

考虑到输入可以被用户中断。在这种情况下,该函数返回数组的初始化元素的数量。

这是一个示范程序。

#include <stdio.h>

size_t read_array( int *a, size_t n )
{
    return n && scanf( "%d", a ) == 1 ? 1 + read_array( a + 1, n - 1 ) : 0;
}   

#define N   10

int main(void) 
{
    int a[N];

    size_t n = read_array( a, N );

    for ( size_t i = 0; i < n; i++ ) printf( "%d ", a[i] );
    putchar( '\n' );

    return 0;
}

如果要输入数字序列

0 1 2 3 4 5 6 7 8 9

然后输出

0 1 2 3 4 5 6 7 8 9

答案 2 :(得分:1)

示例:

int read_array_aux(int *i, int *n) {
  if (i == n) {
    return 0;
  }
  if (scanf("%d", i) != 1) {
    return -1;
  }
  return read_array_aux(i + 1, n);
}

int read_array_aux2(int *a, size_t i, size_t n) {
  if (i == n) {
    return 0;
  }
  if (scanf("%d", a + i) != 1) {
    return -1;
  }
  return read_array_aux2(a, i + 1, n);
}

int read_array(int *a, size_t n) {
  return read_array_aux(a, a + n);
  // return read_array_aux2(a, 0, n);
}

答案 3 :(得分:1)

首先,条件void read_array() { read_head(); read_tail(); } 是错误的。可能这是段错误的原因。

另外,为什么还要为计算索引而烦恼呢?当递归地处理任何类型的列表时,值得掌握列​​表的头部(列表的第一个元素)和尾部(除头部之外的所有内容)的概念。因此,递归填充数组将定义为(伪代码):

void read_array(int *a, int n) {
   if(n<=0) {
       return;
   } else {
       if(scanf("%d", a) == 1) {
           read_array(a+1,n-1);
       }
   }
}

什么是头?它是当前数组的第一个元素。尾巴是什么?数组从下一个元素开始。因此,read_tail相当于read_array,但开头向前移动了一个元素。

最后,将所有内容集中到一个地方:

$(".back, .laptop, .close").click(function(){
    console.log("widget closed");
    if( !$("body").hasClass('widget-opened')){
        $("html").css("overflow-y","auto");
    }
});

答案 4 :(得分:1)

正如其他答案所提到的,您对n的处理会导致问题。您可以从0的基本情况返回sz == 0,否则返回下一个递归调用的结果,如果-1失败,则返回scanf()。在每次递归调用时,递增a并递减sz。应检查调用函数中返回的值是否存在输入错误:成功时为0,失败时为-1

请注意,这是一个tail recursion,应该由大多数优秀的编译器进行优化。

#include <stdio.h>

int read_array(int *a, size_t sz);

int main(void)
{
    int arr[5];
    puts("Enter array elements:");
    if (read_array(arr, 5) != 0) {
        fprintf(stderr, "Input error\n");
    } else {
        for (size_t i = 0; i < 5; i++) {
            printf("%8d", arr[i]);
        }
        putchar('\n');
    }

    return 0;
}

int read_array(int *a, size_t sz)
{
    if (sz == 0 ) {
        return 0;
    }
    if (scanf("%d", a) == 1){
        return read_array(a + 1, sz - 1);
    } else {
        return -1;
    }
}

示例互动:

Enter array elements:
1 2 3 4 5
       1       2       3       4       5

Enter array elements:
1 2 3 x 5
Input error