代码之家  ›  专栏  ›  技术社区  ›  one-zero-zero-one

删除代码重复

  •  9
  • one-zero-zero-one  · 技术社区  · 14 年前

    higher-order functions 对于 List Set s和 Map 我遇到了这个问题:接受集合和返回相同类型集合的函数具有几乎相同的实现,但必须为每个数据结构重新定义- 列表 s、 以及

    例如,下面是 map 函数 列表 学生:

    public static <A, B> List<B> map(
      List<? extends A> xs, 
      Func1<? super A, ? extends B> transformer
    ) {
      List<B> ys = new ArrayList<B>();
      for(A a : xs) {
        ys.add(transformer.apply(a));
      }
      return ys;
    }
    
    public static <A, B> Set<B> map(
      Set<? extends A> xs, 
      Func1<? super A, ? extends B> transformer
    ) {
      Set<B> ys = new HashSet<B>();
      for(A a : xs) {
        ys.add(transformer.apply(a));
      }
      return ys;
    }
    

    A filter 功能:

    public static <A> List<A> filter(
      List<? extends A> xs, 
      Func1<? super A, Boolean> predicate
    ) {
      List<A> ys = new ArrayList<A>();
      for(A a : xs) {
        if(predicate.apply(a)) {
          ys.add(a);
        }
      }
      return ys;
    }
    
    public static <A> Set<A> filter(
      Set<? extends A> xs, 
      Func1<? super A, Boolean> predicate
    ) {
      Set<A> ys = new HashSet<A>();
      for(A a : xs) {
        if(predicate.apply(a)) {
          ys.add(a);
        }
      }
      return ys;
    }
    

    列表 几乎是一样的。

    有很多功能,比如 滤波器 列表 , 地图 ). 这会导致大量代码重复和代码气味。我想知道Java中是否有某种方法可以帮助我避免所有的代码重复。

    任何帮助都将不胜感激。谢谢。

    编辑:

    Func1 接口定义为:

    interface Func1<A, B> {
      public B apply(A a);
    }
    
    5 回复  |  直到 13 年前
        1
  •  4
  •   Tony Morris    14 年前

    Java没有高阶多态性(也就是更高的种类),所以在类型系统中这是不可能的。许多Java程序员都求助于XML和/或反射(即转义类型系统)来解决这一缺陷。

    Scala可以处理这个问题,你所描述的叫做协变函子。这个相当基本的数据类型(以及更多)已经在Scalaz库中实现,并包括java.util文件.*.

    此外,还有更多不是集合的协变函子和更多不是协变函子。

        2
  •  6
  •   Tom Hawtin - tackline    14 年前
    public static <A, B> List<B> map(
      List<? extends A> xs, 
      Func1<? super A, ? extends B> transformer
    ) {
      List<B> ys = new ArrayList<B>();
      map(xy, transformer, ys);
      return ys;
    }
    
    public static <A, B> Set<B> map(
      Set<? extends A> xs, 
      Func1<? super A, ? extends B> transformer
    ) {
      Set<B> ys = new HashSet<B>();
      map(xy, transformer, ys);
      return ys;
    }
    private static <A, B> map(
      Collection<? extends A> xs, 
      Func1<? super A, ? extends B> transformer,
      Iterable<B> ys
    ) {
      for(A a : xs) {
        ys.add(transformer.apply(a));
      }
    }
    

    工作完成了。

    )我不喜欢 A B 作为这类东西的通用参数。)

    或者你可以使用一个工厂:

    public static <A, B> List<B> map(
      List<? extends A> xs, 
      Func1<? super A, ? extends B> transformer
    ) {
      return map(xs, transformer, new CollectionFactory<B, List<B>>() {
          public List<B> create() { return new ArrayList<B>(); }
      });
    }
    
    public static <A, B> Set<B> map(
      Set<? extends A> xs, 
      Func1<? super A, ? extends B> transformer
    ) {
      return map(xs, transformer, new CollectionFactory<B, Set<B>>() {
          public Set<B> create() { return new HashSet<B>(); }
      });
    }
    
    private interface CollectionFactory<E, C extends Collection<E>> {
        C create();
    }
    
    private static <A, B, C extends Collection<B>> C map(
      Iterable<? extends A> xs, 
      Func1<? super A, ? extends B> transformer,
      CollectionFactory<B, C> factory
    ) {
      C ys = factory.create();
      for(A a : xs) {
        ys.add(transformer.apply(a));
      }
      return ys;
    }
    

    如果不是因为 Collection 那你就得装个难看的适配器了。

    Set<String> strs = hashSets().map(things, formatter);
    
    ...
    
    public static <E> Functions<E, Set<E>> hashSets() {
        return new Functions<E, Set<E>>() {
            protected Set<E> createCollections() {
                return new HashSet<E>();
            }
        };
    }
    
    public abstract class Functions<E, C extends Collection<E>> {
        protected abstract C createCollection();
    
        public <S> C map(
          Set<? extends S> xs, 
          Func1<? super S, ? extends E> transformer
        ) {
          C ys = createCollection();
          for(S a : xs) {
            ys.add(transformer.apply(a));
          }
          return ys;
        }
    
        public <S> C filter(
          List<? extends S> xs, 
          Func1<? super S, Boolean> predicate // Predicate<? super S> might be nicer!!
        ) {
          C ys = createCollection();
          for(A a : xs) {
            if(predicate.apply(a)) {
              ys.add(a);
            }
          }
          return ys;
        }
    }
    
        3
  •  4
  •   Community CDub    8 年前

    我不认为你能做得比汤姆在报告中建议的更好 his answer . Java不支持更高种类的类型—这个特性可以帮助您抽象集合类型,从而避免为每个集合类型重复相同的代码。

    Scala支持这个特性,并且在它的标准库中被广泛使用。 This paper 作者adriaanmoors讨论了Scala如何借助更高级的类型避免这种代码重复。

    上述报纸的两张截图:


    alt text


    alt text

        4
  •  2
  •   munificent    14 年前

    我不相信Java的类型系统足够复杂来解决这个问题,但Scala的是。在2.8版本的collections库中,他们构建了一个系统,可以根据您使用的集合自动创建适当类型的集合。所以,如果你打电话 filter List ,它返回一个新的 滤波器 Set 你会得到一个 回来。它这样做的同时仍然只有一个 滤波器

    要了解更多信息,请查看 Traversable 以及使用它的东西。我相信 CanBuildFrom 是很多魔法发生的地方。

        5
  •  1
  •   Johannes Rudolph    14 年前

    T ,使其能够存储该类型的多个实例。这就是为什么所有的单子定律都适用于这里,所以你可以实现 bind return 成员。

    filter 在您的示例中)可以使用 SelectMay 分别绑定。