8

我有两个接口负责保持闭包

这是第一个在 map 操作中保持闭包的方法。

package com.fs;

/**
 * This interface is responsible for holding the closures when it comes to map.
 * It uses two generic types. One for the argument and one for the return type.
 * @param <B> Generic type 
 * @param <A> Generic type
 */
public interface Func<B,A> {
    /**
     * Function prototype m takes an argument of type A and returns a type B.
     * A map operation can produce a different type.
     * @param x of type A
     * @return type B
     */
     B m(A x); 
}

第二个用于过滤操作

package com.fs;


/**
 * This interface is responsible for holding the closures when it comes to filter.
 * @param <A> Generic type 
 */
public interface Pred<A> {
    /**
     * Function prototype m takes an argument of type A and returns a boolean.
     * A filter operation checks every element if it fits a predicate.
     * @param x of type A
     * @return boolean
     */
    boolean m(A x);
}

我有一个名为 CList 的类,它能够使用闭包。

package com.impl.list;

import com.fs.*;

public class CList<T> {
    T head;
    CList<T> tail;

    public CList(T x, CList<T> xs){
        head = x;
        tail = xs;
    }

    static <A,B> CList<B> map(Func<B,A> f, CList<A> xs){
        if(xs==null){
            return null;
        }
        return new CList<>(f.m(xs.head),map(f,xs.tail));
    }

    static <A,B> CList<B> maploop(Func<B,A> f, CList<A> xs){
        //?????
        return null;
    }

    static <A> CList<A> filter(Pred<A> f, CList<A> xs){
        if(xs == null){
            return null;
        }
        if(f.m(xs.head)){
            return new CList<>(xs.head, filter(f,xs.tail));
        }
        return filter(f,xs.tail);
    }

    static <A> int length(CList<A> xs){
        int ans =0;
        while(xs!= null){
            ++ans;
            xs=xs.tail;
        }
        return ans;
    }
}

这是我用闭包实现 CList 的公共接口。

package com.impl.list;

import com.fs.Func;
import com.fs.Pred;

public class CListClient {
    public static CList<Integer> doubleAll(CList<Integer> xs){
        Func<Integer, Integer> df = new Func<Integer, Integer>() {
            @Override
            public Integer m(Integer x) {
                return x * 2;
            }
        };

        return CList.map(df, xs);
    }

    public static int countNs(CList<Integer> xs,final int n){
        Pred<Integer> pf = new Pred<Integer>() {
            @Override
            public boolean m(Integer x) {
                return x==n;
            }
        };
        return CList.length(CList.filter(pf, xs));
    }

    public static CList<Integer> doubleAllloop(CList<Integer> xs){
        Func<Integer, Integer> df = new Func<Integer, Integer>() {
            @Override
            public Integer m(Integer x) {
                return x * 2;
            }
        };

        return CList.maploop(df, xs);
    }
}

还有一个简单的测试器:

package basic;


import com.impl.list.CList;
import com.impl.list.CListClient;
import org.junit.Test;


public class ListTester {

    CList<Integer> intlist_1 = new CList<>(new Integer(1),null);
    CList<Integer> intlist_2 = new CList<>(new Integer(2),intlist_1);
    CList<Integer> intlist_3 = new CList<>(new Integer(3),intlist_2);
    CList<Integer> intlist_4 = new CList<>(new Integer(4),intlist_3);
    CList<Integer> intlist_5 = new CList<>(new Integer(4),intlist_4);
    CList<Integer> intlist = new CList<>(new Integer(5),intlist_5);

    @Test
    public void test_doubleAll(){
        CList<Integer> doubled = CListClient.doubleAll(intlist);
        CList<Integer> doubledloop = CListClient.doubleAllloop(intlist);

    }

    @Test
    public void test_CountNs(){
        int count3s = CListClient.countNs(intlist, 3);
    }
}

我正在尝试将以递归方式实现的 map 函数转换为 while 循环。我把它命名为maploop。这两天让我的大脑受伤。任何提示都会让我非常高兴。我在这里问这个问题是因为有人可能会参加 Dan Grossman 课程并查看示例并尝试实现该功能。我更喜欢提示而不是实际答案。谢谢。

4

2 回答 2

6

将递归函数转换为迭代函数时,您必须检查需要哪种非尾调用状态(如果有)。然后创建一个堆栈并将状态推送到堆栈上,并像使用递归函数一样对其进行编码。如果您在函数中有多个递归调用,您将需要您的新状态项还包含一个值,该值指示您在函数中的哪个点。

在这种情况下,您只有一个递归调用,并且唯一的状态是xs,所以事情非常简单,您不需要自定义状态对象。

这是我做事的方式(未经测试)。

static <A,B> CList<B> maploop(Func<B,A> f, CList<A> xs){
    Stack<CList<A>> stack = new Stack<>();

    while(xs != null){
        stack.push(xs);
        xs = xs.tail;
    }

    CList<a> result = xs;

    while(!stack.empty()){
        xs = stack.pop();
        result = new CList<>(f.m(xs.head), result);
    }

    return result;
}    
于 2013-04-05T15:07:06.513 回答
0

将递归程序转换为迭代程序的标准方法是通过尾递归变体。作为一个非常简单的示例,考虑以下递归阶乘函数来计算N!

int factorial(int x) {
    if (x == 0)
        return 1;
    else
        return x * factorial(x-1);
}

打电话factorial(N);

使这个尾递归涉及添加一个累积变量:

int tailRecursiveFactorial(int x, int y) {
    if (x == 0)
        return y;
    else
        return tailRecursiveFactorial(x-1, x*y);
}

称呼tailRecursiveFactorial(N, 1);

这直接转换为迭代程序:

int x = N;
int y = 1;
while (x != 0) {
    y = x*y;
    x = x-1;
}

当然,您的问题要困难得多,但一般方法应该仍然有效。

于 2013-04-05T15:25:18.370 回答