Function: nthcdr

nthcdr is a function defined in fns.c.

Signature

(nthcdr N LIST)

Documentation

Take cdr N times on LIST, return the result.

Other relevant functions are documented in the list group.

View in manual

Probably introduced at or before Emacs version 30.1.

Shortdoc

;; list
(nthcdr 1 '(one two three))
    => (two three)

Aliases

-drop drop

Source Code

// Defined in /usr/src/emacs/src/fns.c
{
  Lisp_Object tail = list;

  CHECK_INTEGER (n);

  /* A huge but in-range EMACS_INT that can be substituted for a
     positive bignum while counting down.  It does not introduce
     miscounts because a list or cycle cannot possibly be this long,
     and any counting error is fixed up later.  */
  EMACS_INT large_num = EMACS_INT_MAX;

  EMACS_INT num;
  if (FIXNUMP (n))
    {
      num = XFIXNUM (n);

      /* Speed up small lists by omitting circularity and quit checking.  */
      if (num <= SMALL_LIST_LEN_MAX)
	{
	  for (; 0 < num; num--, tail = XCDR (tail))
	    if (! CONSP (tail))
	      {
		CHECK_LIST_END (tail, list);
		return Qnil;
	      }
	  return tail;
	}
    }
  else
    {
      if (mpz_sgn (*xbignum_val (n)) < 0)
	return tail;
      num = large_num;
    }

  EMACS_INT tortoise_num = num;
  Lisp_Object saved_tail = tail;
  FOR_EACH_TAIL_SAFE (tail)
    {
      /* If the tortoise just jumped (which is rare),
	 update TORTOISE_NUM accordingly.  */
      if (BASE_EQ (tail, li.tortoise))
	tortoise_num = num;

      saved_tail = XCDR (tail);
      num--;
      if (num == 0)
	return saved_tail;
      rarely_quit (num);
    }

  tail = saved_tail;
  if (! CONSP (tail))
    {
      CHECK_LIST_END (tail, list);
      return Qnil;
    }

  /* TAIL is part of a cycle.  Reduce NUM modulo the cycle length to
     avoid going around this cycle repeatedly.  */
  intptr_t cycle_length = tortoise_num - num;
  if (! FIXNUMP (n))
    {
      /* Undo any error introduced when LARGE_NUM was substituted for
	 N, by adding N - LARGE_NUM to NUM, using arithmetic modulo
	 CYCLE_LENGTH.  */
      /* Add N mod CYCLE_LENGTH to NUM.  */
      if (cycle_length <= ULONG_MAX)
	num += mpz_tdiv_ui (*xbignum_val (n), cycle_length);
      else
	{
	  mpz_set_intmax (mpz[0], cycle_length);
	  mpz_tdiv_r (mpz[0], *xbignum_val (n), mpz[0]);
	  intptr_t iz;
	  mpz_export (&iz, NULL, -1, sizeof iz, 0, 0, mpz[0]);
	  num += iz;
	}
      num += cycle_length - large_num % cycle_length;
    }
  num %= cycle_length;

  /* One last time through the cycle.  */
  for (; 0 < num; num--)
    {
      tail = XCDR (tail);
      rarely_quit (num);
    }
  return tail;
}