Clojure手动查找序列中的第n个元素

时间:2014-09-10 15:24:01

标签: clojure functional-programming

我是clojure的新手(以及函数式编程),我试图做一些基本的问题。我试图在没有递归的情况下找到序列中的第n个元素。

类似

(my-nth '(1 2 3 4) 2) => 3

我很难在列表中循环并在找到第n个元素时返回。我尝试了很多不同的方法,而我最终得到的代码是

(defn sdsu-nth
 [input-list n]
 (loop [cnt n tmp-list input-list]
    (if (zero? cnt)
       (first tmp-list)
       (recur (dec cnt) (pop tmp-list)))))

这给了我一个例外,说“不能从空列表中弹出”

我不需要代码,但如果有人能指出我正确的方向,它会真的有用!

6 个答案:

答案 0 :(得分:3)

您正在使用函数pop,它对不同的数据结构具有不同的行为。

user> (pop '(0 1 2 3 4))
(1 2 3 4)
user> (pop [0 1 2 3 4])
[0 1 2 3]
user> (pop (map identity '(0 1 2 3 4)))
ClassCastException clojure.lang.LazySeq cannot be cast to clojure.lang.IPersistentStack  clojure.lang.RT.pop (RT.java:640)

此外,您正在调用popfirst的来电。如果是迭代,请使用peek / popfirst / rest作为对,将两者混合会导致意外结果。 first / rest是最低的共同标准,如果您想要对各种顺序类型进行推广,请使用它们,如果可以的话,它们会强制序列生效。

user> (first "hello")
\h
user> (first #{0 1 2 3 4})
0
user> (first {:a 0 :b 1 :c 2})
[:c 2]

使用您的功能,将pop替换为rest,我们会得到预期的结果:

user> (defn sdsu-nth
        [input-list n]
        (loop [cnt n tmp-list input-list]
              (if (zero? cnt)
                  (first tmp-list)
                (recur (dec cnt) (rest tmp-list)))))

#'user/sdsu-nth
user> (sdsu-nth (map identity '(0 1 2 3 4)) 2)
2
user> (sdsu-nth [0 1 2 3 4] 2)
2
user> (sdsu-nth '(0 1 2 3 4) 2)
2
user> (sdsu-nth "01234" 2)
\2

答案 1 :(得分:2)

您真正想要做的是使用内置的nth函数,因为它完全符合您的要求: http://clojuredocs.org/clojure_core/clojure.core/nth

然而,既然你正在学习这仍然是一个很好的练习。你的代码实际上适合我。确保你给它一个列表而不是矢量 - pop用向量做了不同的事情(它返回的矢量没有最后一个项而不是第一个 - see here)。

答案 2 :(得分:2)

列表作为nth,最多可以使用(fn [list_nums n] (last (take (inc n) list_nums))) ,然后返回最后一个#(last (take (inc %2) %1)) 元素。

(= (#(last (take (inc %2) %1)) '(4 5 6 7) 2) 6) ;; => true

或者:

package com.example.brian.inventoryapp;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static android.R.layout.simple_list_item_1;


public class MainActivity extends AppCompatActivity {
    public static final int CREATE_REQUEST = 1;

    private Button addButton;
    private ListView itemListView;
    private ArrayList<String> arrayList;
    private ArrayAdapter<String> arrayAdapter;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        itemListView = (ListView) findViewById(R.id.itemListView);
        addButton = (Button) findViewById(R.id.addButton);

        arrayAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, arrayList);
        itemListView.setAdapter(arrayAdapter);

        addButton = (Button) findViewById(R.id.addButton);
        addButton.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, CreateActivity.class);
                startActivityForResult(intent, CREATE_REQUEST);
            }

        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == CREATE_REQUEST) {
            if (resultCode == RESULT_OK) {
                String item = data.getStringExtra("data");
                arrayList.add(item);
                arrayAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, arrayList);
                itemListView.setAdapter(arrayAdapter);
            }
        }
    }
}

证明:

createButton

答案 3 :(得分:1)

如果提供的索引不等于或大于序列的长度(您已实现零索引nth),则您的代码适用于列表。如果tmp-listcnt变为零之前变为空,则会出现此错误。

使用矢量不能很好地工作:

user> (sdsu-nth [1 2 3 4] 2)
;; => 1
user> (sdsu-nth [10 2 3 4] 2)
;; => 10

它似乎为每个提供的索引返回0元素。正如noisesmith注意到它发生的原因是pop因为内部结构而对矢量的工作方式不同。对于向量pop,将从结尾删除元素,然后first返回任何向量的第一个值。

如何修复:使用rest代替pop,以便在应用于列表和向量时消除函数行为的差异。

答案 4 :(得分:1)

(fn [xs n]
  (if (= n 0)
    (first xs)
    (recur (rest xs) (dec n))))

答案 5 :(得分:0)

我想到这样做的另一种方式是让它真正非递归(即没有for / recur)是

(defn sdsu-nth
 [input-list n]
 (if (zero? (count input-list))
    (throw (Exception. "IndexOutOfBoundsException"))
    (if (>= n (count input-list))
       (throw (Exception. "IndexOutOfBoundsException"))
       (if (neg? n)
          (throw (Exception. "IndexOutOfBoundsException"))
          (last (take (+ n 1) input-list))))))