我可以在scala repl中创建匿名函数,如下所示:
scala> val a = (x: Int) => x * x
a: Int => Int = <function1>
但是有没有看到它创建后的内部?
我正在考虑我将采用函数并返回函数的情况。我只是好奇地看到repl创建的是返回值,而不仅仅是返回值的类型,所以类似于:
scala> val b = (f: (Int => Boolean)) => (x: Int) => ! (f(x))
b: (Int => Boolean) => (Int => Boolean) = <function1>
scala> val c = b((x: Int) => x % 2 == 0)
c: Int => Boolean = <function1>
我想知道在c!
中生成了什么代码答案 0 :(得分:10)
它不像same functionality in, say, clojure那样方便,因为它显示已编译的代码,但您可以利用public void yourFunction(object sender,Eventargs e)
{
string args = ((LinkButton)sender).CommandArgument.ToString();
// rest of code here
}
:
:javap
或指示REPL在使用scala> :javap -help
usage :javap [opts] [path or class or -]...
-help Prints this help message
-raw Don't unmangle REPL names
-app Show the DelayedInit body of Apps
-fun Show anonfuns for class or Class#method
-verbose/-v Stack size, number of locals, method args
-private/-p Private classes and members
-package Package-private classes and members
-protected Protected classes and members
-public Public classes and members
-l Line and local variable tables
-c Disassembled code
-s Internal type signatures
-sysinfo System info of class
-constants Static final constants
scala> :javap -s a
Compiled from "<console>"
public class {
public static final MODULE$;
descriptor: L;
public static {};
descriptor: ()V
public scala.Function1<java.lang.Object, java.lang.Object> a();
descriptor: ()Lscala/Function1;
public ();
descriptor: ()V
}
进行编译时输出代码内部结构(虽然它可能过于冗长,也许有人可以建议使用较少冗长的编译器阶段)。
BTW,正如你在javap输出中看到的那样,每个REPL表达式都隐含地包含在周围的代码中,不要混淆 - scala不能正常执行它。
答案 1 :(得分:5)
这是在REPL之外完成的,但您可以使用带有-print选项的scalac编译代码。执行scalac -help
将为您提供选项的说明:
-print Print program with Scala-specific features removed.
我尝试了这个小程序:
object Test {
def main(args: Array[String]): Unit = {
val a = (x: Int) => x * x
}
}
它给了我这个输出:
$ scalac -print Test.scala
[[syntax trees at end of cleanup]] // Test.scala
package <empty> {
object Test extends Object {
def main(args: Array[String]): Unit = {
val a: Function1 = {
(new <$anon: Function1>(): Function1)
};
()
};
def <init>(): Test.type = {
Test.super.<init>();
()
}
};
@SerialVersionUID(value = 0) final <synthetic> class anonfun$1 extends scala.runtime.AbstractFunction1$mcII$sp with Serializable {
final def apply(x: Int): Int = anonfun$1.this.apply$mcII$sp(x);
<specialized> def apply$mcII$sp(x: Int): Int = x.*(x);
final <bridge> <artifact> def apply(v1: Object): Object = scala.Int.box(anonfun$1.this.apply(scala.Int.unbox(v1)));
def <init>(): <$anon: Function1> = {
anonfun$1.super.<init>();
()
}
}
}
答案 2 :(得分:5)
要查看函数文字,请使用:javap -fun
:
$ scala
Welcome to Scala version 2.11.7 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_51).
Type in expressions to have them evaluated.
Type :help for more information.
scala> def f = (1 to 10) map (_ * 2)
f: scala.collection.immutable.IndexedSeq[Int]
scala> :javap -fun f
Compiled from "<console>"
public final class $anonfun$f$1 extends scala.runtime.AbstractFunction1$mcII$sp implements scala.Serializable {
public static final long serialVersionUID;
public final int apply(int);
public int apply$mcII$sp(int);
public final java.lang.Object apply(java.lang.Object);
public $anonfun$f$1();
}
这是传递给map的匿名函数。
要过滤apply函数的主体,请使用尾随#
或f#apply
:
scala> :javap -fun f#
public final int apply(int);
public int apply$mcII$sp(int);
public final java.lang.Object apply(java.lang.Object);
这包括专门的方法。
scala> :javap -fun -prv f#
public final int apply(int);
flags: ACC_PUBLIC, ACC_FINAL
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: iload_1
2: invokevirtual #21 // Method apply$mcII$sp:(I)I
5: ireturn
LocalVariableTable:
Start Length Slot Name Signature
0 6 0 this L$line3/$read$$iw$$iw$$anonfun$f$1;
0 6 1 x$1 I
LineNumberTable:
line 10: 0
public int apply$mcII$sp(int);
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: iload_1
1: iconst_2
2: imul
3: ireturn
LocalVariableTable:
Start Length Slot Name Signature
0 4 0 this L$line3/$read$$iw$$iw$$anonfun$f$1;
0 4 1 x$1 I
LineNumberTable:
line 10: 0
public final java.lang.Object apply(java.lang.Object);
flags: ACC_PUBLIC, ACC_FINAL, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: invokestatic #32 // Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
5: invokevirtual #34 // Method apply:(I)I
8: invokestatic #38 // Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
11: areturn
LocalVariableTable:
Start Length Slot Name Signature
0 12 0 this L$line3/$read$$iw$$iw$$anonfun$f$1;
0 12 1 v1 Ljava/lang/Object;
LineNumberTable:
line 10: 0
scala>
对于val,首先看一下评估的构造函数:
scala> :javap -fun c
Failed: No closures found.
scala> :javap -prv c
[snip]
public $line7.$read$$iw$$iw$();
flags: ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
0: aload_0
1: invokespecial #20 // Method java/lang/Object."<init>":()V
4: aload_0
5: putstatic #22 // Field MODULE$:L$line7/$read$$iw$$iw$;
8: aload_0
9: getstatic #27 // Field $line6/$read$$iw$$iw$.MODULE$:L$line6/$read$$iw$$iw$;
12: invokevirtual #30 // Method $line6/$read$$iw$$iw$.b:()Lscala/Function1;
15: new #32 // class $line7/$read$$iw$$iw$$anonfun$1
18: dup
19: invokespecial #33 // Method $line7/$read$$iw$$iw$$anonfun$1."<init>":()V
22: invokeinterface #39, 2 // InterfaceMethod scala/Function1.apply:(Ljava/lang/Object;)Ljava/lang/Object;
[snip]
然后剪切/粘贴anonfun的名称,可选择添加#
以仅过滤apply方法:
scala> :javap -prv $line7/$read$$iw$$iw$$anonfun$1#
public final boolean apply(int);
flags: ACC_PUBLIC, ACC_FINAL
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: iload_1
2: invokevirtual #18 // Method apply$mcZI$sp:(I)Z
5: ireturn
LocalVariableTable:
Start Length Slot Name Signature
0 6 0 this L$line7/$read$$iw$$iw$$anonfun$1;
0 6 1 x I
LineNumberTable:
line 12: 0
public boolean apply$mcZI$sp(int);
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: iload_1
1: iconst_2
2: irem
3: iconst_0
4: if_icmpne 11
7: iconst_1
8: goto 12
11: iconst_0
12: ireturn
[snip]
存储在c
中的函数:
scala> $intp.isettings.unwrapStrings = false
$intp.isettings.unwrapStrings: Boolean = false
scala> c.getClass
res4: Class[_ <: Int => Boolean] = class $line3.$read$$iw$$iw$$anonfun$1$$anonfun$apply$1
scala> :javap -prv $line3.$read$$iw$$iw$$anonfun$1$$anonfun$apply$1
[snip]
-raw
的{{1}}选项公开了REPL的包和包装对象。要在普通输出中查看它们,您必须关闭输出过滤,如图所示。
或者:
:javap
通常,anon funs很简短,因此不需要过滤apply方法。
请注意,为了转换到Java 8 lambdas,我删除了此功能,因为编码一直在变化。也许它会在某个时候恢复。
答案 3 :(得分:3)
在运行时没有什么能够很好地打印编译的代码。
您可以编写一个宏来打印树的源代码并使用它吗?大多数宏教程都以一个用于打印源代码的宏开头 - 参见例如http://www.warski.org/blog/2012/12/starting-with-scala-macros-a-short-tutorial/
也许:
// Given a partial function "pf", return the source code for pf
// as a string as well as the compiled, runnable function itself
def functionAndSource(pf: PartialFunction[Any, Any]): (String, PartialFunction[Any, Any]) = macro functionAndSourceImpl
def functionAndSourceImpl = ...
val pm1: (String, PartialFunction[Any, Any]) = functionAndSource {
case "foo" => R1
}
Scala中的这种情况永远不会那么简单或好看。 Scala不是Lisp或Ruby:它是一种编译语言,并没有针对代码本身的反射进行优化。
(有关非常相似的问题,请参阅Scala Pattern Matching pretty printed。)
答案 4 :(得分:3)
试试我的小scala-to-java工具。它符合给定的scala源,然后使用Procyon反编译器将其反编译为java。
对于你的scala输入:
val a = (x: Int) => x * x
它显示了这个反编译的输出:
import scala.*;
import scala.runtime.*;
public final class _$$anon$1$$anonfun$1 extends AbstractFunction1$mcII$sp implements Serializable {
@Override
public final int apply(final int x) {
return this.apply$mcII$sp(x);
}
@Override
public int apply$mcII$sp(final int x) {
return x * x;
}
}
import scala.*;
public final class _$$anon$1 {
private final Function1<Object, Object> a = new _$$anon$1$$anonfun._$$anon$1$$anonfun$1(this);
private Function1<Object, Object> a() {
return (Function1<Object, Object>)this.a;
}
}