当前位置: 首页 > 编程日记 > 正文

C#和Java的闭包-Jon谈《The Beauty of Closures》

第一段略。。。

大多数讲闭包的文章都是说函数式语言,因为它们往往对闭包的支持最完善。当你在使用函数式语言时,很可能已经清楚了解了什么是闭包,所以我想写一篇在经典OO语言出现的闭包有什么用处应该也是很合适的事情。这篇文章我准备讲一下C#(1、2、3)和JAVA(7以前版本)的闭包。

什么是闭包?

简单来讲,闭包允许你将一些行为封装,将它像一个对象一样传来递去,而且它依然能够访问到原来第一次声明时的上下文。这样可以使控制结构、逻辑操作等从调用细节中分离出来。访问原来上下文的能力是闭包区别一般对象的重要特征,尽管在实现上只是多了一些编译器技巧

利用例子来观察闭包的好处(和实现)会比较容易, 下面大部份内容我会使用一个单一的例子来进行讲解。例子会有JAVA和C#(不同版本)来说明不同的实现。所有的代码可以点这里下载。

PS:维基百科上对闭包的解释就很经典:

在计算机科学中,闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。

Peter J. Landin 在1964年将术语闭包定义为一种包含环境成分和控制成分的实体

需求场景:过滤列表

按一定条件过滤某个列表是很常见的需求。虽然写几行代码遍历一下列表,把满足条件的元素挑出来放到新列表的“内联”方式很容易满足需求,但把判断逻辑提取出来还是比较优雅的做法。唯一的难点就是如何封装“判定一个元素是否符合条件”逻辑,闭包正好可以解决这个问题。

虽然我上面说了“过滤”这个词,但它可能会有两个截然不同的意思“把元素滤出来放到列表里”或者把“把元素滤出来扔掉”。比如说“偶数过滤”是把“偶数”保留下来还是过滤掉?所以我们使用另一个术语“断言”。断言就是简单地指某样东西是不是满足某种条件。在我们的例子中即是生成一个包含了原列表满足断言条件的新列表。

在C#中,比较自然地表现一个断言就是通过delegate,事实上C# 2.0有一个 bool Predicate<T> delegate。(顺带一提,因为某些原因,LINQ更偏向于Func<T, bool> delegate;我不知道这是为什么,相关的解释也很少。然而这两个泛型类的作用其实是一样的。)在Java中没有delegate,因此我们会使用只有一个方法的interface。当然C#中我们也可以使用interface,但会使得代码看起来很混乱,而且不能使用匿名函数和Lamba(拉姆达)表达式-C#中符合闭包特征的实现。下面的interface/delegate供大家参考:

//Declaration for System.Predicate<T> in C Sharp
public delegate bool Predicate<T>(T obj)

//Predicate.java
public interface Predicate<T>
{
    boolean match(T item);
}

在两种语言中过滤用的代码都比较简单,得先说明在这里我会避免使用C#的Extension Method来让代码看起来更加简单明了。-但是使用过LINQ的人要注意where这个Extension Method。(它们的延迟执行有些区别,但这里我会避免触及)

// In ListUtil.cs
static class ListUtil
{
    
public static IList<T> Filter<T>(IList<T> source, Predicate<T> predicate)
    {
        List
<T> ret = new List<T>();
        
foreach (T item in source)
        {
            
if (predicate(item))
            {
                ret.Add(item);
            }
        }
        
return ret;
    }
}
// In ListUtil.java
public class ListUtil
{
    
public static <T> List<T> filter(List<T> source, Predicate<T> predicate)
    {
        ArrayList
<T> ret = new ArrayList<T>();
        
for (T item : source)
        {
            
if (predicate.match(item))
            {
                ret.add(item);
            }
        }
        
return ret;
    }
}

(两种语言中我都写了一个Dump方法用来输出指定list的内容)

现在我们已经定义好“过滤”的方法,接下来就是要调用它。为了演示闭包的重要作用,我会先使用一个简单的不需要使用到闭包都能解决的案例,然后再进一步到比较难的案例。

过滤案例1:找出长度较短的字符串(固定长度)

我们的需求场景都会比较简单基础,但希望大家能从中看出它们的不同之处。我们将会有一个字符串list,然后根据这个list生成另一个只包含长度较“短”(Length<4)的字符串list。建立list很简单-建立断言才是难点。

在C# 1.0中只能通过单独的方法来表现一个断言逻辑,然后再创建一个delegate指向该方法。(当然由于代码使用了泛型并不能真地在C# 1.0下面通过编译,但要注意delegate实例是如何被建立的-这是重点)

// In Example1a.cs
static void Main()
{
    Predicate
<string> predicate = new Predicate<string>(MatchFourLettersOrFewer);
    IList
<string> shortWords = ListUtil.Filter(SampleData.Words, predicate);
    ListUtil.Dump(shortWords);
}
static bool MatchFourLettersOrFewer(string item)
{
    
return item.Length <= 4;
}

在C# 2.0中有三种方式实现:

第一,使用上面一样的代码;

第二,利用方法组转换(Method Group Conversion)对代码进行简化;

PS:In C# 2.0 includes a feature called method group conversion  which allows you to assign the name of a method to a delegate, without the use new or explicitly invoking the delegate's constructor.

使用方法组转换比较浪费时间-它只是把new Predicate<string>(MatchFourLettersOrFewer) 变成了 MatchFourLettersOrFewer:

// In Example1a.cs
static void Main()
{
//Method group conversion Predicate
<string> predicate = MatchFourLettersOrFewer;IList<string> shortWords = ListUtil.Filter(SampleData.Words, predicate);ListUtil.Dump(shortWords); }

第三,利用匿名函数将断言直接写在调用上下文中

相对方法组转换而言,匿名委托要有趣得多:

static void Main()
{
    Predicate
<string> predicate = delegate(string item)
        {
            
return item.Length <= 4
;
        };
    IList<string> shortWords = ListUtil.Filter(SampleData.Words, predicate);
    ListUtil.Dump(shortWords);
}

这样一来,就不再需要一个外部独立的方法用来封装断言逻辑,并且,断言放在了被使用的点上。很好很强大。它背后是怎么工作的呢?如果你用ildasm或者reflector去看一下生成的代码,你会发现其实它与第一个版本产生的代码很大程度是一样的,编译器只是帮我们完成了某些工作。稍后我们会看到它更强悍的能力。

在C# 3.0中除了有上面三种方式,还有拉姆达表达式。对于本文来讲,拉姆达表达式只是匿名函数的一个简化形式。(这两种东东最大的区别在于LINQ中的拉姆达表达式能被转换成表达式树,但这与本文无关)使用拉姆达表达式:

static void Main()
{
    Predicate
<string> predicate = item => item.Length <= 4;
    IList
<string> shortWords = ListUtil.Filter(SampleData.Words, predicate);
    ListUtil.Dump(shortWords);
}

由于在右边使用了<=,看起来像是有个大箭头指着item.Length,但为了保持前后一致,只好请大家将就着看了。这里其实可以写成等价的Predicate<string> predicate = item => item.Length < 5

在Java中没有delegate-只能实现上面定义的interface。最简单的方法就是定义一个类并实现该interface,如:

// In FourLetterPredicate.java
public class FourLetterPredicate implements Predicate<String>
{
    
public boolean match(String item)
    {
        
return item.length() <= 4;
    }
}
// In Example1a.java
public static void main(String[] args)
{
    Predicate
<String> predicate = new FourLetterPredicate();
    List
<String> shortWords = ListUtil.filter(SampleData.WORDS, predicate);
    ListUtil.dump(shortWords);
}

这里没有使用任何华丽的语言特性,为了实现一点小小的逻辑,它使用了一整个独立的类。根据Java的惯例,类应该放在单独的文件里,这使得程序的可读性变差。当然可以使用嵌套类的方式来避免这种问题,但逻辑还是离开了使用它的地方-相当于啰嗦版的C# 1.0解决方案。(这里不打算给出嵌套版的实现代码,有需要的朋友可以看看打包代码里面Example1b.java。Java可以通过匿名类把代码书写成内联的方式(这就是Java目前支持的闭包,在匿名类的光芒照射下,代码进化了:

// In Example 1c.java

public static void main(String[] args)
{
    Predicate
<String> predicate = new Predicate<String>()
    {
        
public boolean match(String item)
        {
            
return item.length() <= 4
;
        }
    };

    List
<String> shortWords = ListUtil.filter(SampleData.WORDS, predicate);
    ListUtil.dump(shortWords);
}

如你所见,比起C# 2.0和C# 3.0的代码,这个显得还是比较啰嗦了点,但至少代码被放在了它应该在的地方。这就是Java目前支持的闭包……接下来本文进入第二个例子。

过滤案例2:找出长度较短的字符串(可变长度)

目前为止我们的断言并不需要访问到原来的“上下文”-长度是硬编码的,然后字符串是以参数的形式传进去的。现在,需求变动一下,允许用户指定多长的字符串才算是合适的。

首先,我们回到C# 1.0。它其实不支持真正的闭包-找不到一块简单的地方来存储我们需要的变量。当然,我们可以在当前方法的上下文中声明一个变量来解决这个问题(比如利用静态成员变量),但这明显不是一个好的解决方法-理由只有一个,类马上变成了线程不安全的。解决的方法就是不要把状态存储在当前上下文中,转而存储在新建的类中。这么一来,代码看起来跟原来的Java代码非常相似,区别只是这里使用delegate,而Java使用interface。

// In VariableLengthMatcher.cs
public class VariableLengthMatcher
{
    
private int maxLength;
    
public VariableLengthMatcher(int maxLength)
    {
        
this.maxLength = maxLength;
    }
    
/// <summary>
    
/// Method used as the action of the delegate
    
/// </summary>
    public bool Match(string item)
    {
        
return item.Length <= maxLength;
    }
}
// In Example2a.cs
static void Main()
{
    Console.Write(
"Maximum length of string to include? ");
    
int maxLength = int.Parse(Console.ReadLine());

    VariableLengthMatcher matcher 
= new VariableLengthMatcher(maxLength);
    Predicate
<string> predicate =
 matcher.Match;
    IList
<string> shortWords = ListUtil.Filter(SampleData.Words, predicate);
    ListUtil.Dump(shortWords);

}

相对来说,C# 2.0和C# 3.0的改动要小得多:只需将硬编码的常量改成变量即可。先不管这背后的原理-一会看完Java版的代码后再来研究这个问题。

// In Example2b.cs (C# 2)
static void Main()
{
    Console.Write(
"Maximum length of string to include? ");
    
int maxLength = int.Parse(Console.ReadLine());

    Predicate
<string> predicate = delegate(string item)
    {
        
return item.Length <= maxLength;
    };
    IList
<string> shortWords = ListUtil.Filter(SampleData.Words, predicate);
    ListUtil.Dump(shortWords);
}
// In Example2c.cs (C# 3)
static void Main()
{
    Console.Write(
"Maximum length of string to include? ");
    
int maxLength = int.Parse(Console.ReadLine());

    Predicate
<string> predicate = item => item.Length <= maxLength;
    IList
<string> shortWords = ListUtil.Filter(SampleData.Words, predicate);
    ListUtil.Dump(shortWords);
}

Java版的代码(使用了匿名类的那个版本)改动也比较简单,但有一点不爽的是-必须把参数声明为final。了解其原理前先来看一下代码:

// In Example2a.java
public static void main(String[] args) throws IOException
{
    System.out.print(
"Maximum length of string to include? ");
    BufferedReader console 
= new BufferedReader(new InputStreamReader(System.in));
    
final int maxLength = Integer.parseInt(console.readLine());
    
    Predicate
<String> predicate = new Predicate<String>()
    {
        
public boolean match(String item)
        {
            
return item.length() <= maxLength;
        }
    };
    
    List
<String> shortWords = ListUtil.filter(SampleData.WORDS, predicate);
    ListUtil.dump(shortWords);
}

那么,C#和Java的代码到底有什么不同呢?在Java中,变量的被匿名类捕获。在C#中,变量本身被delegate捕获。为了证明C#捕获了变量本身,我们来改一下C# 3.0的代码,使变量的值在变量过滤后发生改变,看看改变是否反映到下一次过滤:

// In Example2d.cs
static void Main()
{
    Console.Write(
"Maximum length of string to include? ");
    
int maxLength = int.Parse(Console.ReadLine());

    Predicate
<string> predicate = item => item.Length <= maxLength;
    IList
<string> shortWords = ListUtil.Filter(SampleData.Words, predicate);
    ListUtil.Dump(shortWords);

    Console.WriteLine(
"Now for words with <= 5 letters:");
    maxLength 
= 5;
    shortWords 
= ListUtil.Filter(SampleData.Words, predicate);
    ListUtil.Dump(shortWords);
}

注意,我们只是改变局部变量的值,而并没有重新创建delegate的实例,或者其它等价的操作。由于delegate其实是直接访问这个局部变量,所以其实它是能够知道变量发生的变化。再进一步,接下来在断言逻辑中直接对变量进行修改:

// In Example2e.cs
static void Main()
{
    
int maxLength = 0;

    Predicate
<string> predicate = item => { maxLength++return item.Length <= maxLength; };
    IList
<string> shortWords = ListUtil.Filter(SampleData.Words, predicate);
    ListUtil.Dump(shortWords);
}

我不打算再深入地讲这些是怎么实现的-《C# in Depth》第5章讲的都是这些细节。只是希望你们一些对“局部变量”的观念认识被完全颠倒。

我们已经看到了C#是如何对捕获的变量进行修改的,那Java呢?答案只有一个:你不能对捕获的变量进行修改。它已经被声明为final,所以这个问题其实是很无厘头的。而且就算你人品值爆糟,突然间能对该变量进行更改,也会发现断言逻辑根本对修改毫无反应。变量的值在断言声明的时候被拷贝并存储到匿名类内。不过,对于引用变量,它的成员发生改变还是能够被知道的。比如说,如果你引用了一个StringBuilder,然后对它进行Append操作,那在匿名类中是可以看到StringBuilder的改变。

对比捕获策略:复杂性VS功能

明显Java的设计局限性比较大,但也同时也比较容易理解,不容易发生概念混淆的情况,局部变量的行为和一般情况下没什么不同,大多数情况下,代码看起来也更简单易懂。比如下面的代码,利用Java runable interface和.NET Action delegate-两个都是会执行一些操作,不需要参数,也不返回任何值。首先看看C#的代码:

// In Example3a.cs
static void Main()
{
    
// First build a list of actions
    List<Action> actions = new List<Action>();
    
for (int counter = 0; counter < 10; counter++)
    {
        actions.Add(() 
=> Console.WriteLine(counter));
    }

    
// Then execute them
    foreach (Action action in actions)
    {
        action();
    }
}

会输出些什么?其实我们只声明了一个counter变量-所以其实所有的Action捕获的都是同一个counter变量。结果就是每一行都输出数字10。为了把代码“修正”到我们预期的效果(如输出0到9),则需要在循环体中使用另一个局部变量:

// In Example3b.cs
static void Main()
{
    
// First build a list of actions
    List<Action> actions = new List<Action>();
    
for (int counter = 0; counter < 10; counter++)
    {
        
int copy = counter;
        actions.Add(() 
=> Console.WriteLine(copy));
    }

    
// Then execute them
    foreach (Action action in actions)
    {
        action();
    }
}

这样,每次循环体在执行的时候,都会取得一份counter的拷贝,而不是它本身-所以每个Action取得了不同的变量值。如果看一下编译器生成的代码,你就会完全明白这种结果是合情合理的,但这对于大多数第一次看到代码的程序员来说,其直觉得出的结果往往是相反的。(包括我)

在Java中则完全不存在第一个例子的情形-你根本不可能捕获到counter变量,因为它并没有被声明为final。使用final变量,最终得到下面类似C#的代码:

public static void main(String[] args)
{
    
// First build a list of actions
    List<Runnable> actions = new ArrayList<Runnable>();        
    
for (int counter=0; counter < 10; counter++)
    {
        
final int copy = counter;
        actions.add(
new Runnable()
        {
            
public void run()
            {
                System.out.println(copy);
            }
        });
    }
    
    
// Then execute them
    for (Runnable action : actions)
    {
        action.run();
    }
}

有了“捕获变量的值”语义存在,代码显得清晰明了,更符合直觉。尽管代码看起来比较啰嗦没有C#那么爽,但Java强制只能使用唯一正确的方式去书写代码。但同时当你需要像原来C#代码的那种行为时(有时候确实有这种需求),用Java实现起来是会比较麻烦。(可以用一个只有一个元素的数组,然后引用这个数组,再对数组元素进行操作,代码看起来会比较杂乱)。

我到底想讲些什么?

在例子中,我们可以看到了闭包好处其实不多。当然,我们把控制结构和断言逻辑成功分拆开来,但这并没有使代码比原来的更加简洁。这种事经常发生,新特性在简单的情形往往是看起来没想像中那么好,有那么大的作用。闭包通常带来的好处,是可组合性,如果你觉得这么说有些扯淡,没错-这也是问题的一部份。当你对闭包运用很熟练甚至有些迷恋的时候,两者之间的联系就会变得越来越明显,否则是不容易看出其玄妙所在。

闭包不是被设计来提供可组合性。它做的不过是让delegate实现起来更加简单(或者只有一个方法的interface,下面统一用delegate简称)。如果没有闭包,直接写一个循环结构其实是比把封装了一些相关逻辑的delegate传给另一个方法去执行循环要来得简单。即使可以通过delegate调用“在已有类中添加的方法”,最终你还是没办法把逻辑代码放在最合适的地方,而且没了闭包提供的信息存储便利,则必须依靠方法外部的上下文来存储某些信息。

可见,闭包使delegate更加易用。这就意味着值得将API设计成为使用delegate的形式。(我认为这种情况并不适用于.NET 1.1下面基本上只能用来处理线程和订阅事件的delegate)当你开始用delegate的方式去解决问题时,如何去做变得显而易见。比如,最常见的就是创建一个用AND或者OR(也包括其它逻辑操作符)将两个断言串连起来的Predicate<T>。

当把某个delegate产生的结果装填进另一个列表,或者对delegate进行加工产生新的,就会有完全不同的组合方式,如果将逻辑当作可以被传递的某种数据来考虑时,所有不同类型的选择都是可行的。

这种编码方式的好处远不止上面说的那么多-整个LINQ都是基于这种方式。我们创建的过滤器只是一个可以将有序数据转换成另一组数据的例子。另外还有排序,分组,联接另一组数据和Projecting等操作。使用传统的编码方式去写这些操作虽不是非常痛苦的事情,但是如果“数据管道”中转换操作越来越多时,复杂性随之提高,另外,LINQ赋于对象延迟执行和数据流的能力,这种一次循环执行多次操作方式明显比多次循环执行一次操作要节约很多内存。即使每一个单独的转换操作被设计得很聪明高效,复杂性上还是依旧无法取得平衡-通过闭包封装简明扼要的代码片断以及良好设计的API带来的组合能力可以很好去除复杂性。

结论

刚开始接触闭包,可能不会对它有深刻印象。当然,它使得你的interface或者delegate实现起来更简单(取决于语言)。其威力只有在相关类库利用了它的特性之后才能体现出来,允许你将自定义行为放在合适的地方。当同一个类库同时允许你将几个简单的步骤以比较自然的方式组合起来实现一些重要行为时,其复杂性也只是几个步骤的总和-而不是大于这个总和。我不是赞同某些人鼓吹的可组合性是解决复杂性的银弹,但它肯定是很有用的技巧,而且由于闭包使得它在很多地方可以得以实施。

拉姆达表达式最重要特点就是简洁。看一下之前的Java和C#的代码,Java的代码显然比较笨拙冗长。很多Java闭包的倡议都是想解决这个问题。稍后我会发一篇文章讲一下我对这些不同倡议的看法。

原文URL:http://www.cnblogs.com/klesh/archive/2008/05/15/the-beauty-of-closures.html

E文URL:http://csharpindepth.com/Articles/Chapter5/Closures.aspx

转载于:https://www.cnblogs.com/jeriffe/articles/1733157.html

相关文章:

谷歌浏览器输入框背景颜色变黄的解决方案

2019独角兽企业重金招聘Python工程师标准>>> input:-webkit-autofill, input:-webkit-autofill:hover, input:-webkit-autofill:focus { box-shadow:0 0 0 60px #eee inset; -webkit-text-fill-color: #878787; } 转载于:https://my.oschina.net/kitty0107/blog/296…

男人最不该做的7件事

1.没有目标 2.浪费时间 3.不独立 4.被动地活着 5.不规划自己的人生 6.不学习吸收信息 7.不接受爱情转载于:https://www.cnblogs.com/jiu0821/p/4315660.html

1085 Perfect Sequence

明确题目的核心是要找到 找到第一个满足 M > m*p 的M的下标。然后用该下标减去起点的下标即为序列元素个数。 二分区间应当是M所有可能的取值范围。起点是i1&#xff0c;终点是N而不是N-1&#xff0c;虽然A[N]上无元素。注意啊&#xff0c;原题要找的M是小于等于m*p的&…

[笔记]Go语言在Linux环境下输出彩色字符

Go语言要打印彩色字符与Linux终端输出彩色字符类似&#xff0c;以黑色背景高亮绿色字体为例&#xff1a; fmt.Printf("\n %c[1;40;32m%s%c[0m\n\n", 0x1B, "testPrintColor", 0x1B) 其中0x1B是标记&#xff0c;[开始定义颜色&#xff0c;1代表高亮&#xf…

javascript中this那些事

定义 this是函数执行的上下文。 调用方式 1. 作为函数调用&#xff0c;指向window&#xff08;严格模式报undefined的错&#xff09;。 var namehello; function a() { console.log(this.name) } a(); //hellovar c{ name:haha, d: function(){ a(); } } c.d(…

java序列化的作用-这个挺有用的,不妨学学

http://bbs.tech.ccidnet.com/read.php?tid249048 最近在阅读Core J2EE Patterns 的时候发现例子里用于在各个层次里进行传输的TO&#xff08;Data Transfer Object&#xff09;都实现了java.io.Serializable接口&#xff0c;看到这些偶突然感到茅塞顿开&#xff5e;困扰了很久…

二分法典例:木棒切割问题

Input : 输入木棒根数n&#xff0c;要得到的等长木棒数量K&#xff0c;以及n根木棒的长度。 Output : 等长木棒的最大长度。 用二分法求解这道题&#xff0c;首先要找到以得到的等长木棒数量为因变量、等长木棒长度为自变量函数。 int getK(int l){//随着l增大&#xff0c;返…

对Android 开发者有益的 40 条优化建议(转)

下面是开始Android编程的好方法&#xff1a; 找一些与你想做事情类似的代码 调整它&#xff0c;尝试让它做你像做的事情 经历问题 使用StackOverflow解决问题对每个你像添加的特征重复上述过程。这种方法能够激励你&#xff0c;因为你在保持不断迭代&#xff0c;不经意中你学到…

英语语法总结--连词

连词 连词是一种虚词&#xff0c; 它不能独立担任句子成分而只起连接词与词&#xff0c;短语与短语以及句与句的作用。连词主要可分为两类&#xff1a;并列连词和从属连词。并列连词用来连接平行的词、词组和分 句。如&#xff1a;and, but, or, nor, so, therefore, yet, howe…

开放平台鉴权以及OAuth2.0介绍

OAuth 2.0 协议 OAuth是一个开发标准&#xff0c;允许用户授权第三方网站或应用访问他们存储在另外的服务提供者上的信息&#xff0c;而不需要将用户名和密码提供给第三方网站或分享他们数据的内容。OAuth 2.0不兼容1.0。协议的参与者 RO (resource owner): 资源所有者&#xf…

1044 Shopping in Mars

这题我写了两个二分函数。 BS借用的模板是找到第一个大于等于总价的商品下标&#xff0c;然后返回的是钻石价值和减去商品总价&#xff0c;通过遍历来得到最小的差值&#xff0c;注意遍历的最后一个数字的时候可能会返回负值&#xff0c;所以只有当返回值大于等于0才可以用来竞…

nodeJS之eventproxy源码解读

1.源码缩影 !(function (name, definition) { var hasDefine typeof define function, //检查上下文环境是否为AMD或CMD hasExports typeof module ! undefined && module.exports; //检查上下文环境是否为Node if (hasDefine) { define(definition); //AMD环境或CM…

解决phpmyadmin3.4空密码登录被禁止登陆的方法

很多时候我们在本机测试时会将root用户密码设置为空。因为我把php升级到了5.3.1&#xff0c;以前的phpmyadmin版本不能用了&#xff0c;就升级到phpMyAdmin 3.2.4版的时候&#xff0c;会遇到无法以空密码登录root用户的情况。怎么解决呢? 请参照如下步骤&#xff1a; 1、打开程…

CentOS安装新版RabbitMQ解决Erlang 19.3版本依赖

2019独角兽企业重金招聘Python工程师标准>>> 通过yum等软件仓库都可以直接安装RabbitMQ&#xff0c;但版本一般都较为保守。 RabbitMQ官网提供了新版的rpm包&#xff08;http://www.rabbitmq.com/download.html&#xff09;&#xff0c;但是安装的时候会提示需要erl…

1048 Find Coins(二分法解法)

非常基础的二分法-寻找序列中是否存在某一条件的元素 的应用 AC代码 #include<cstdio> #include<iostream> #include<set> #include<vector> #include<map> #include<algorithm>using namespace std;const int SUP 100000000; const in…

关于chrome等浏览器不支持showModalDialog的解决方案

目前&#xff0c;新版本的chrome和opera、Firefox等浏览器已经不支持showModalDialog方法。 如果是没有接收返回值的&#xff0c;可以直接将window.showModalDialog改为window.open。 需要接收返回值的情况&#xff1a; 父页面设置&#xff1a; var uIdName; function chooseus…

Flex Javascript 交互实现代码

关键字&#xff1a;ExternalInterface所用类库&#xff1a;SWFObject/*** Flex调用Javascript函数* params functionName:String Javascript函数名称* params ...params Javascript函数参数* return 返回Javascript函数的return内容**/ExternalInterface.call(functionName:Str…

C#反射使用时注意BindingFlags的用法(转载)

最近刚刚开始用反射做项目&#xff0c;遇到一个小的知识点&#xff0c;记录一下。 c#反射查找方法时&#xff0c;默认只能查到public方法。如果想要查找private方法&#xff0c;需要设定BindingFlags. 即&#xff1a; BindingFlags.Public|BindingFlags.Instance 默认…

1126 Eulerian Path

主要考英语或者数学基础。 一幅连通图的奇点个数为0或2时才能够被一笔画。 连通图的判断用DFS来计数。 连通图0个奇点&#xff1a;Eulerian 连通图2个奇点&#xff1a;semi-Eulerian 非连通图/连通图其他数量的奇点&#xff1a;non-Eulerian AC代码 #include<cstdio&…

各种小的 dp (精)

Q~ 抛一枚硬币 n 次&#xff0c;每次可能是正面或者反面向上&#xff0c;求没有连续超过 k 次硬币向上的方案数 A &#xff1a; dp[ i ] 表示到 i 位置的方案数&#xff0c; 1 . 当 i < k 时&#xff0c; dp[i] dp[i-1]*2 2 . 当 i k 时&#xff0c; dp[i] dp[i-1]*2 - 1…

写给还在大学的兄弟姐妹

看到软件专业毕业生之一个月攻略 这篇文章之后&#xff0c;忽然想起了自己两个多月前找工作时的写的一篇文章&#xff0c;便拿出来与大家分享。这仅是个人的一些看法&#xff0c;不正确之处还请各位指出&#xff0c;有砖尽管拍。 基础很重要 许多企业招聘&#xff0c;要求大学…

grunt学习

1、http://javascript.ruanyifeng.com/tool/grunt.html Grunt&#xff1a;任务自动管理工具 2、转载于:https://www.cnblogs.com/king-bj/p/4322794.html

IP地址和MAC地址

MAC地址又称硬件地址&#xff0c;是MAC帧的头部&#xff0c;在数据链路层只能看见MAC地址。 IP地址是逻辑地址&#xff0c;是IP数据报的头部&#xff0c;路由器根据IP地址进行路由选择。 IP地址为4个字节32位&#xff0c;编制经历了3个历史阶段。 MAC地址为6个字节48位。

ie9下console不兼容的问题

最近在调整项目在ie9下的展示问题&#xff0c;发现在ie9下&#xff0c;js文件不执行&#xff0c;打开控制台才执行&#xff0c;原因是ie9不支持console&#xff0c;以下给出两种解决方案&#xff1a;1. 在webpack.prod.conf.js 中添加并修改js插件配置项&#xff08;我用的是we…

[转载] linux、Solaris下xdmcp远程桌面服务

原文链接 http://youlvconglin.blog.163.com/blog/static/52320420106243857254/ 使用图形界面远程登录linux和Solaris&#xff0c;首先要在服务端开启xdmcp服务&#xff0c;windows下使用xmanager连接 Ubuntu下则使用下默认也安装了该客户端&#xff0c;一次打开[应用程序]-[…

3.2.4 控制图层显示的范围

为了是地图更加简洁 和 减小地图负载 &#xff0c;达到分级显示某些图层的效果&#xff0c;应该为每一个图层设置 合理的 可见比例尺范围。1 begin2 3 aeMapMain.Layer[2].MaximumScale :500000;//最大可见比例尺 分母4 aeMapMain.Layer[2].MinimumScale :1000000;//最小可见比…

1103 Integer Factorization 需再做

本题是典型的DFS剪枝 我对DFS有了更深的认识&#xff1a;整个过程就是一片森林(根节点不唯一)的生长&#xff0c;到了界限就得到结果并返回或者得不到结果也返回&#xff0c;DFS的参数存放的是所有需要积累的变量。 提示&#xff1a; 1. 最外层的while或者for可以看成是一个…

POJ1001--Exponentiation(幂计算)翻译

Exponentiation幂计算Time Limit: 500MSMemory Limit: 10000KTotal Submissions: 141868Accepted: 34673 Description 描述 Problems involving the computation of exact values of very large magnitude and precision are common. 高精度、大数值的计算问题是很常见的&…

datatable无法设置横向滚动条(设置无效)

datatable设置横向滚动条无效 js如下&#xff1a; 页面如下&#xff1a; 设置 scrollx 属性为true时&#xff0c;还需在 table 添加 style"white-space: nowrap; "最终效果&#xff1a; 转载于:https://www.cnblogs.com/renzp/p/10069594.html

水晶报表导出数据并实现打印

要在里一个页面上进行操作 ReportDocument rdocument new ReportDocument(); //公用打印方法 ExportCrystalL ExCrystal new ExportCrystalL(); User u new User(); #region 加载页面 protected void Page_Load(object sender, EventArgs e) { if (!IsPostB…