bubble sort
尼尔·福特(Neal Ford)创造了“ 功能思维 ”一词来描述主要受面向对象编程培训的开发人员所需要的思维转变,他们希望将功能性编程概念和技术集成到他们的实践中。 我在分为两部分的教程“ 面向Java开发人员的函数式编程 ”中应用了此概念。 在第1部分中,我介绍了用JavaScript编写的五种功能编程技术,然后在第2部分中,使用等效的Java代码重构了这些代码示例。 接下来,我们再次使用Java 8中引入的功能性编程语言功能,重构了许多示例。
现在,我邀请您进行功能性思维的最后练习,将Sort应用程序从“ Java开发人员的功能性编程,第2部分 ”中重构为集成各种功能性技术。 我们将再次进行几次迭代,以改进代码。
重构#1:经典的气泡排序,具有功能性
下面显示的原始Sort
应用程序远非功能正常。 关于它唯一起作用的是sort()
方法的参数列表。 多亏了传递的比较器,该列表表明sort()
是一流的函数。
清单1.原始的,面向对象的冒泡排序(Sort.java)
import java.util.Comparator;
public class Sort
{
public static void main(String[] args)
{
String[] innerplanets = { "Mercury", "Venus", "Earth", "Mars" };
dump(innerplanets);
sort(innerplanets, new Comparator<String>()
{
@Override
public int compare(String e1, String e2)
{
return e1.compareTo(e2);
}
});
dump(innerplanets);
sort(innerplanets, new Comparator<String>()
{
@Override
public int compare(String e1, String e2)
{
return e2.compareTo(e1);
}
});
dump(innerplanets);
}
static <T> void dump(T[] array)
{
for (T element: array)
System.out.println(element);
System.out.println();
}
static <T> void sort(T[] array, Comparator<T> cmp)
{
for (int pass = 0; pass < array.length - 1; pass++)
for (int i = array.length - 1; i > pass; i--)
if (cmp.compare(array[i], array[pass]) < 0)
swap(array, i, pass);
}
static <T> void swap(T[] array, int i, int j)
{
T temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
回想一下,函数式编程是面向表达式的,并且不使用using语句。 牢记这些准则,很明显,我们可以通过消除dump()
的for
语句和sort()
的for
和if
语句来使Sort
应用程序更具功能性。
我们将需要更换for
和if
有相当的高阶函数( 又名一流的功能)。 因为我们可能想重用这些功能,所以我们将它们粘贴在库中。 清单2展示了一个FL
(功能库)类。
清单2.使用高阶函数和函数库重构的排序(FL.java)
interface Function<T>
{
void apply(T t);
}
public final class FL
{
private final static Function<Integer> FEMPTY =
new Function<Integer>() { public void apply(Integer i) {} };
public static void forEach(final int start, final int end, final boolean inc,
final Function<Integer> f)
{
ifThen(start != end,
new Function<Integer>()
{
@Override
public void apply(Integer i)
{
f.apply(start);
forEach(inc ? start + 1 : start - 1, end, inc, f);
}
});
}
public static void ifThen(boolean predicate, Function<Integer> onTrue)
{
ifThenElse(predicate, onTrue, FEMPTY);
}
public static void ifThenElse(boolean predicate, Function<Integer> onTrue,
Function<Integer> onFalse)
{
ifThenElse1(predicate ? onTrue : onFalse);
}
private static void ifThenElse1(Function<Integer> f)
{
f.apply(0);
}
}
关于代码
清单2首先定义一个Function
类型。 此类型应该是public
并且应位于其自己的源文件中,因为它将从库外部进行访问。 但是,将其放置在与FL
类相同的源文件中很方便。
FL
类声明了代表高阶函数的public
forEach()
, ifThen()
和ifThenElse()
方法。 我不需要ifThenElse()
进行此转换,但在其他情况下可能会派上用场。
forEach()
传递到其start
, end
, inc
和f
参数的参数调用forEach()
方法:
-
start
是循环的初始整数索引。 -
end
比循环的最终索引多一。 -
inc
对于索引递增循环为true
,对于索引递减循环为false
。 -
f
是为每次循环迭代执行的函数。
递归和?:运算符
forEach()
使用递归来处理循环,这在函数式编程中很常见。 它调用ifThen()
方法处理基本情况,在这种情况下,当start
等于end
时,递归end
。
ifThen
方法通过传递给predicate
和onTrue
参数来调用。 我们确实应该给predicate
一个函数类型,但是在这种情况下,为了方便起见,我们将其保留为boolean
。
回想一下清单2,我以ifThen()
的方式实现了ifThenElse()
,我认为在启动项目时可能需要它。 因为ifThen()
没有else
部分,所以我们现在将默认的FEMPTY
函数传递给ifThenElse()
的onFalse
参数。
ifThenElse()
方法使用?:
运算符来决定onTrue
或onFalse
函数。 因为此运算符必须返回一个值,并且由于我们希望ifThenElse()
具有void
返回类型(以避免避免必须指定return
语句),所以我们编写ifThenElse()
来调用ifThenElse1()
帮助函数。 然后,助手使用参数0
调用函数参数的apply()
方法。
我们本可以设计ifThen()
和ifThenElse()
来接受一个附加参数,该参数最终将传递给apply()
,但对于函数排序应用程序,我们不需要此功能。
我们正在引入副作用吗?
在第1部分中 ,我提到了副作用,在创建功能程序时要避免这些副作用。 副作用的一个示例是赋值语句通过更改变量的存储值来对其进行突变。 ifThenElse1()
调用包含一个赋值语句,其中onTrue
或onFalse
被分配给ifThenElse1()
的f
参数。 但是,在这种情况下,我认为这不是不好的做法,因为f
是一个局部变量,在多线程上下文中(每个线程都有自己的局部变量副本)不会引起问题。 但是,不要只听我的话:Stack Exchange有一个关于使用局部可变变量进行函数编程的信息性线程 。
重构#2:Java 8之前的功能Sort应用程序
我们已经重构了原始的Sort
应用程序,并提供了一些功能性的编程功能。 现在,我们准备编写一个功能齐全的Sort应用程序。 在这种情况下,我们将使用Java 8之前的函数式编程技术。 清单3给出了FuncSort
应用程序的源代码。
清单3.功能排序应用程序,版本1(FuncSort.java)
import java.util.Comparator;
public class FuncSort
{
public static void main(String[] args)
{
String[] innerplanets = { "Mercury", "Venus", "Earth", "Mars" };
dump(innerplanets);
sort(innerplanets, new Comparator<String>()
{
@Override
public int compare(String e1, String e2)
{
return e1.compareTo(e2);
}
});
dump(innerplanets);
sort(innerplanets, new Comparator<String>()
{
@Override
public int compare(String e1, String e2)
{
return e2.compareTo(e1);
}
});
dump(innerplanets);
}
static <T> void dump(final T[] array)
{
FL.forEach(0, array.length, true,
new Function<Integer>()
{
@Override
public void apply(final Integer i)
{
System.out.println(array[i]);
}
});
System.out.println();
}
static <T> void sort(final T[] array, final Comparator<T> cmp)
{
FL.forEach(0, array.length - 1, true,
new Function<Integer>()
{
@Override
public void apply(final Integer pass)
{
FL.forEach(array.length - 1, pass, false,
new Function<Integer>()
{
@Override
public void apply(final Integer i)
{
FL.ifThen(cmp.compare(array[i],
array[pass]) < 0,
new Function<Integer>()
{
@Override
public void apply(Integer z)
{
swap(array, i, pass);
}
});
}
});
}
});
}
static <T> void swap(T[] array, int i, int j)
{
T temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
假设清单2和清单3中的源文件位于同一目录中,请按如下所示进行编译:
javac *.java
运行生成的应用程序,如下所示:
java FuncSort
您应该观察到与在第2部分中的“ Sort
应用程序中看到的相同输出。
dump()
和sort()
方法肯定比清单2中的方法更具功能性。 它们也更冗长,更难阅读。
重构#3:针对Java 8的功能排序
在前面的示例中,我们使用功能编程技术将面向对象的代码转换为功能更强的范例。 现在,让我们看看使用Java 8的功能编程功能生成功能全面的Java Sort应用程序时会发生什么。
清单4. FuncSort.java(版本2)
import java.util.Arrays;
import java.util.Comparator;
public class FuncSort
{
public static void main(String[] args)
{
String[] innerplanets = { "Mercury", "Venus", "Earth", "Mars" };
dump(innerplanets);
sort(innerplanets, (e1, e2) -> e1.compareTo(e2));
dump(innerplanets);
sort(innerplanets, (e1, e2) -> e2.compareTo(e1));
dump(innerplanets);
}
static <T> void dump(T[] array)
{
Arrays.stream(array).forEach(System.out::println);
System.out.println();
}
static <T> void sort(T[] array, Comparator<T> cmp)
{
System.arraycopy(Arrays.stream(array).sorted(cmp).toArray(), 0, array, 0,
array.length);
}
}
关于代码
dump()
和sort()
方法很简单,因为我们利用了Java的Streams API。 Arrays.stream(array)
表达式将数组转换为流。 sorted()
中间操作返回一个新流,该新流由旧流的元素组成,并根据提供的Comparator
进行排序。 toArray()
终端操作返回一个包含已排序流元素的数组。
结论
到现在为止,您可能已经发现我在设置此示例方面作弊。 清单4中功能齐全的程序并未实现Java 8版本的冒泡排序算法,就像我所说的那样:相反,它依赖于sorted()
操作使用的任何算法。 冒泡排序不仅效率低下,而且该算法的功能齐全的编程版本将不必要地冗长。 真正的功能性思维可以使功能代码高效而简洁,同时仍然可以理解。
这个故事“编写纯功能的Bubble Sort应用程序”最初由JavaWorld发布 。
翻译自: https://www.infoworld.com/article/3319783/write-a-purely-functional-bubble-sort-application.html
bubble sort