Jaa


每周源代码26- LINQ的正则表达式和Processing 在Javascript中的运用

原文发表地址 The Weekly Source Code 26 - LINQ to Regular Expressions and Processing in Javascript

原文发表时间 2008-05-10 12:57

我对开发人员如何运用插件和其他东西来扩展应用程序越来越感兴趣了。沿袭我一贯的要求勤读代码以成为更高水平的程序开发者,亲爱的读者,我现在向你们展示"每周源代码."一系列帖子中的第26个(将近半年了)。

有时候读代码,我简直想踢自己(当然是在脑子里想想而已啦),然而骂自己;“笨蛋,我早就应该想到的!”。然后觉得自己真的不算是好的编程员。然后将源代码深深地记在脑海中。

这周我一直在阅读一些优秀的编程员写的代码,其实这些代码我也应该能想到的。巧的是,这些示例都是关于语言移植或在另外一种语言中重新构造。

JavaScript的“Processing ”

我说的“Processing”是一个开放源码的基于Java的可视化程序语言。请参阅https://processing.org/.Jeff觉得它“较之编程,它更类似于草图编辑 。”我觉得它是用代码进行草图编辑。Jeff渴盼道:“真希望有一天,网页都是用Ben Fry创建的漂亮的动态可视化图像来显示说明。”

clip_image002

John Resig自从开发了他的力作JQuery之后,可以说是被誉赞为世界上最优秀的Javascript 编程员之一。他将Processing移植到Javascript中,并给我们带来了Processing.js.

你可以通过两种方式与它进行交互。第一种,是作为一个优雅而紧凑的Javascript API:

    1: var p = Processing(CanvasElement);
    2: p.size(100, 100);
    3: p.background(0);
    4: p.fill(255);
    5: p.ellipse(50, 50, 50, 50);

或者,你也可以这样发掘真正的Processing语言:

    1: Processing(CanvasElement, "size(100, 100); background(0);" + "fill(255); ellipse(50, 50, 50, 50);"); 

此版本是专门针对所有未发行的试用版浏览器如Firefox3, Opera 9.5 和Webkit Nightlies (Safari)而发布的。我正尝试在Silverlight中的DLR下用Javascript来实现。嘿嘿。

下面是他的演示。请记住,这些在IE7上不能用。

* 91 basic demos.

* 51 larger, topical, demos.

* 4 custom "in the wild" demos.

演示有一大堆,但这个演示特别强大,管用。一个17行代码的工作时钟。

    1: void setup() {  
    2:   size(200, 200);
    3:   stroke(255);
    4:   smooth();
    5: }
    6: void draw() {
    7:   background(0);
    8:   fill(80);
    9:   noStroke();
   10:   // Angles for sin() and cos() start at 3 o'clock;
   11:   // subtract HALF_PI to make them start at the top
   12:   ellipse(100, 100, 160, 160);
   13:   float s = map(second(), 0, 60, 0, TWO_PI) - HALF_PI;
   14:   float m = map(minute(), 0, 60, 0, TWO_PI) - HALF_PI;
   15:   float h = map(hour() % 12, 0, 12, 0, TWO_PI) - HALF_PI;
   16:   stroke(255);
   17:   strokeWeight(1);
   18:   line(100, 100, cos(s) * 72 + 100, sin(s) * 72 + 100);
   19:   strokeWeight(2);
   20:   line(100, 100, cos(m) * 60 + 100, sin(m) * 60 + 100);
   21:   strokeWeight(4);
   22:   line(100, 100, cos(h) * 50 + 100, sin(h) * 50 + 100);
   23: } 

他的代码在很大程度上受到Canvas的影响,这就是为什么不能在IE7上使用的原因。大部分processing.js文件从一个API(API处理)映射到Javascript 构造,大多数情况下是canvas.例如,建一个点(x,y) :

    1: p.point = function point( x, y )
    2: {
    3:   var oldFill = curContext.fillStyle;
    4:   curContext.fillStyle = curContext.strokeStyle;
    5:   curContext.fillRect( Math.round( x ), Math.round( y ), 1, 1 );
    6:   curContext.fillStyle = oldFill;
    7: }

请注意,这是个1*1的矩形。很有趣吧,但那只是API映射器的寿命。亲爱的读者,记得哪天要提醒我告诉你,我是怎样让填好的饼状图在一个既不支持Put/GetPixel ,又不支持浮点运算的Original Palm Pilot上工作。这只是个梦罢了!

不管怎么说,这个家伙真正的过人之处是他的三角函数。记住,这是一个处理函数,他不但实现了它,还彻底地完成了构件。

开始:

    1: p.triangle = function triangle( x1, y1, x2, y2, x3, y3 )
    2: {
    3:   p.beginShape();
    4:   p.vertex( x1, y1 );
    5:   p.vertex( x2, y2 );
    6:   p.vertex( x3, y3 );
    7:   p.endShape();
    8: }

很明显,对吧?好吧,其实不是这样的。你想想,2DCanvas并不拥有那三个更高级别方法的任意一种。Begin 和EndShape是非常清晰的。但是为此他必须实现一个好的Fill,Stroke 和ClosePath方法去让它清晰。

    1: p.beginShape = function beginShape( type )
    2: {
    3:   curShape = type;
    4:   curShapeCount = 0;
    5: }
    6: p.endShape = function endShape( close )
    7: {
    8:   if ( curShapeCount != 0 )
    9:   {
   10:     curContext.lineTo( firstX, firstY );
   11:      if ( doFill )
   12:       curContext.fill();
   13:            if ( doStroke )
   14:       curContext.stroke();
   15:        curContext.closePath();
   16:     curShapeCount = 0;
   17:     pathOpen = false;
   18:   }
   19:    if ( pathOpen )
   20:   {
   21:     curContext.closePath();
   22:   }
   23: }

大概有四层深,把每个基元构建在另一个基元上直到得到一个漂亮,整洁的三角函数实现。然后他可以将其运用到quad()中,bezierVertex的操作方法亦相同。这样可以帮助你通过使用FireBug工具的方法来解读他的代码。从一个清楚地知道自己在做什么的绅士那重新学习Javascript是一个非常美妙有趣的方法。

LINQ to RegEx和Fluent正则表达式

本周为了一个小任务我一直在不断的学习再学习正则表达式。有趣的是,尽管有一大堆正则表达式工具,我还是在第一时间内把正则表达式忘得一干二净。Josh Flanagan提出了一个正则表达式的流畅用户界面:

    1: Regex socialSecurityNumberCheck = new Regex(@"^\d{3}-?\d{2}-?\d{4}$");

如下所示:

    1: Regex socialSecurityNumberCheck = new Regex(Pattern.With.AtBeginning
    2:          .Digit.Repeat.Exactly(3)
    3:          .Literal("-").Repeat.Optional
    4:          .Digit.Repeat.Exactly(2)
    5:          .Literal("-").Repeat.Optional 
    6:          .Digit.Repeat.Exactly(4)
    7:          .AtEnd);

我只花了一秒钟完成,好吧,确实花了些时间。深呼吸一分钟,然后大声地把它读出来。事实上还蛮管用的。而且针对Josh的帖子中的评论也是有合理的说法的。

它让我联想到,如果该库的用户只知道简单地使用正则表达式,那么他们还.需要了解一个相当复杂的语法,作为正则表达式,这个语法与“普通英语”相差甚远。

当然没错,尝试新事物也是件趣事。如果你看下他的原文,你会发现他的串联字符串确实很高明。我觉得不管是对于讲授或学习正则表达式,这都是个很有趣的方法,尤其你是像我一样散漫的正则表达式学习者。

Krzysztof Koźmic在2007创建了类似的API。他的流畅的用户界面的正则表达式是这样的:

    1: Pattern pattern = Pattern.Define().
    2:   As("Kot".Count(Times.AtLeast(2))).
    3:   FollowedBy(Any.Except('a','b','c')).
    4:   Start(At.BeginingOfStringOrLine);

然后,Roy Osherove进一步推进了Josh的API,从2006年开始把Josh的流畅的用户界面运用到正则表达式中。并在创建LINQ to Regex的过程中运用了LINQ查询语法。

下面是Roy的示例:

    1: public void FindEmailUsingPattern()
    2: {
    3:   var query = from match in
    4:       RegexQuery.Against("sdlfjsfl43r3490r98*(*Email@somewhere.com_dakj3j")
    5:                where match.Word.Repeat.AtLeast(1)
    6:                    .Literal("@")
    7:                    .Word.Repeat.AtLeast(1)
    8:                    .Literal(".")
    9:                    .Choice.Either(
   10:                         Pattern.With.Literal("com"),
   11:                         Pattern.With.Literal("net"))
   12:                    .IsTrue()
   13:                select match;
   14:    foreach (var match in query)
   15:    {
   16:        Assert.AreEqual("Email@somewhere.com",match.Value);
   17:    }
   18: }

"from match in"后面的核心是Roy的静态Against( )调用,返回了RegexQuery(是IEnumerable < Match>),接着在后面支持foreach:

    1: namespace Osherove.LinqToRegex
    2: {
    3:     public class RegexQuery : IEnumerable<match>
    4:     {
    5:        private readonly string input;
    6:        private object lastPatternRetVal;
    7:        private RegexQuery(string input)
    8:        {
    9:           this.input = input;
   10:        }
   11:        public static RegexQuery Against(string input)
   12:        {
   13:           return new RegexQuery(input);
   14:        }
   15:        private string _regex;
   16:        public RegexQuery Where(Expression<func><pattern,bool> predicate)
   17:        { 
   18:           _regex = new PatternVisitor().VisitExpression(predicate).ToString();
   19:           return this;
   20:        }
   21:        public RegexQuery Select<t>(Expression<func><pattern,t> selector)
   22:        {
   23:           return this;
   24:        }
   25:        #region IEnumerable Members
   26:        IEnumerator IEnumerable.GetEnumerator()
   27:        {
   28:           return ((IEnumerable<match>)this).GetEnumerator();
   29:        }
   30:        public IEnumerator<match> GetEnumerator()
   31:        {
   32:           MatchCollection matches = Regex.Matches(input, _regex);
   33:           foreach (Match found in matches)
   34:           {
   35:              yield return found;
   36:           }
   37:        }
   38:     }
   39:  }
   40: </match></match></match>

你可以在the source for Roy's project up at his assembla.com project siteJosh's source is on his blog上参阅。值得注意的是,无需任何技巧,你也可以结合LINQ查询和正则表达式。因为Matches会在MatchCollection中返回。LINQ喜欢Ienumerable的东西。

你可以使用LINQ投射将对象从匹配集中退出来:

    1: List<yourType> = (from Match m in matches
    2:             select new YourType
    3:             {
    4:                Id = m.Groups[1].Value,
    5:                Something = m.Groups[2].Value
    6:             }).ToList();

所以,任何事物都有两面性。一方面,你可以通过标准方式或流畅用户界面创建正则表达式。也可以以字符串结束。另一方面,你可以提取信息。大多数情况下,你会关心返回的MatchCollection。通常,你可能想把信息退出来,所以当你’在集合上执行foreach语句时,不管你是否一开始就创建了查询。你可以使用LINQ创建一个对象投射,对其进行分类,然后用查询对它们进行分组。

这个办法真不错!