مطالب
تبدیل PDF به تصویر با استفاده از API توکار Window 8.1 در برنامه‌های غیر مترو دات نت
ویندوز 8.1 دارای امکانات و API توکاری جهت نمایش و خواندن فایل‌های PDF در برنامه‌های مترو است. در ادامه قصد داریم از این امکانات در یک برنامه‌ی متداول دات نت، برای مثال یک برنامه‌ی کنسول غیر مترو استفاده کنیم.


آماده سازی برنامه‌های دات نت برای دسترسی به API مترو ویندوز 8.1

ابتدا یک برنامه‌ی کنسول دات نت 4.5.1 را آغاز کنید. برای دسترسی به API ویندوز 8.1 حتما نیاز است که حداقل از دات نت 4.5.1 شروع کرد. سپس برنامه را در VS.NET بسته و فایل پروژه آن‌را در یک ادیتور متنی باز کنید.
در ابتدای فایل csproj، نیاز است سطر TargetPlatformVersion ذیل اضافه شود.
  <PropertyGroup>
    <TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
    <TargetPlatformVersion>8.1</TargetPlatformVersion>
  </PropertyGroup>
سپس در همین فایل، ارجاعات زیر را نیز اضافه نمائید:
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.ComponentModel.DataAnnotations" />
    <Reference Include="System.Core" />
    <Reference Include="System.ObjectModel" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Data" />
    <Reference Include="System.Xml" />
    <Reference Include="System.Threading" />
    <Reference Include="System.Threading.Tasks" />
  </ItemGroup>
  <ItemGroup>
    <Reference Include="Windows" />
    <Reference Include="System.Runtime" />
    <Reference Include="System.Runtime.WindowsRuntime" />
  </ItemGroup>
مواردی مانند System.Runtime، System.Runtime.WindowsRuntime امکان دسترسی به API ویندوز 8 را در برنامه‌های دات نت میسر می‌کنند.


یک نکته
اگر می‌خواهید این فرآیند را ساده و خودکار کنید، از قالب‌های پروژه‌ی مخصوص DesktopWinRT.Templates.vsix استفاده نمائید.
DesktopWinRT.Templates.vsix


افزودن ارجاعی به Nito.AsyncEx

چون برنامه‌ی مورد استفاده کنسول است و API ویندوز 8 کاملا async طراحی شده‌است، نیاز است با کمک AsyncContext موجود در کتابخانه‌ی Nito.AsyncEx بتوان از امکانات async و await در متد Main برنامه استفاده کرد. البته اگر از سایر برنامه‌های دسکتاپ استفاده می‌کنید، فقط کافی است امضای متد رخدادن گردان را به async تغییر دهید.
 install-package Nito.AsyncEx


تبدیل استریم‌های دات نت به استریم‌های WinRT

اکثر متدهای WinRT با استریم‌هایی از نوع IRandomAccessStream کار می‌کنند. برای اینکه بتوان استریم استاندارد دات نت را به این نوع تبدیل کرد، می‌توان از کلاس‌های ذیل کمک گرفت:
using System;
using System.IO;
using Windows.Storage.Streams;

namespace ConsoleWin81PdfApiTest
{
    public static class MicrosoftStreamExtensions
    {
        public static IRandomAccessStream AsRandomAccessStream(this Stream stream)
        {
            return new RandomStream(stream);
        }

    }

    class RandomStream : IRandomAccessStream
    {
        readonly Stream _internstream;

        public RandomStream(Stream underlyingstream)
        {
            _internstream = underlyingstream;
        }

        public IInputStream GetInputStreamAt(ulong position)
        {
            _internstream.Position = (long)position;
            return _internstream.AsInputStream();
        }

        public IOutputStream GetOutputStreamAt(ulong position)
        {
            _internstream.Position = (long)position;
            return _internstream.AsOutputStream();
        }

        public ulong Size
        {
            get
            {
                return (ulong)_internstream.Length;
            }
            set
            {
                _internstream.SetLength((long)value);
            }
        }

        public bool CanRead
        {
            get { return _internstream.CanRead; }
        }

        public bool CanWrite
        {
            get { return _internstream.CanWrite; }
        }

        public IRandomAccessStream CloneStream()
        {
            throw new NotSupportedException();
        }

        public ulong Position
        {
            get { return (ulong)_internstream.Position; }
        }

        public void Seek(ulong position)
        {
            _internstream.Seek((long)position, SeekOrigin.Begin);
        }

        public void Dispose()
        {
            _internstream.Dispose();
        }

        public Windows.Foundation.IAsyncOperationWithProgress<IBuffer, uint> ReadAsync(IBuffer buffer, uint count, InputStreamOptions options)
        {
            return GetInputStreamAt(Position).ReadAsync(buffer, count, options);
        }

        public Windows.Foundation.IAsyncOperation<bool> FlushAsync()
        {
            return GetOutputStreamAt(Position).FlushAsync();
        }

        public Windows.Foundation.IAsyncOperationWithProgress<uint, uint> WriteAsync(IBuffer buffer)
        {
            return GetOutputStreamAt(Position).WriteAsync(buffer);
        }
    }
}
تا اینجا به یک متد الحاقی جدیدی به نام AsRandomAccessStream می‌رسیم که امکان تبدیل استریم استاندارد دات نت را به IRandomAccessStream مخصوص WinRT دارد. از آن می‌توان برای باز کردن یک فایل و ارسال استریم آن به توابع WinRT و یا ثبت استریم WinRT در یک فایل استفاده کرد.


خواندن فایل‌های PDF و تبدیل صفحات آن‌ها به تصویر

در ادامه کد کامل استفاده از API جدید ویندوز 8.1 را جهت خواندن فایل‌های PDF ملاحظه می‌کنید. این امکانات جدید در فضای نام Windows.Data.Pdf قرار دارند و صرفا امکان خواندن فایل‌های PDF را تدارک دیده‌اند.
using System;
using System.IO;
using System.Threading.Tasks;
using Windows.Data.Pdf;
using Nito.AsyncEx;

namespace ConsoleWin81PdfApiTest
{
    class Program
    {
        static void Main(string[] args)
        {
            AsyncContext.Run(async () =>
            {
                await test();
            });
        }

        private static async Task test()
        {
            using (var randomAccessStream = File.Open("PieChartPdfReport.pdf", FileMode.Open).AsRandomAccessStream())
            {
                var pdfDocument = await PdfDocument.LoadFromStreamAsync(randomAccessStream);
                for (uint i = 0; i < pdfDocument.PageCount; i++)
                {
                    using (var page = pdfDocument.GetPage(i))
                    {
                        /*var renderOptions = new PdfPageRenderOptions
                        {
                            BackgroundColor = Colors.LightGray,
                            DestinationHeight = (uint) (page.Size.Height*10)
                        };*/

                        using (var stream = File.Open(string.Format("page-{0}.png", i + 1), FileMode.OpenOrCreate).AsRandomAccessStream())
                        {
                            await page.RenderToStreamAsync(stream/*, renderOptions*/);
                            await stream.FlushAsync();
                        }
                    }
                }
            }
        }
    }
}

توضیحات:
- متد AsyncContext.Run جزو امکانات Nito.AsyncEx است و امکان نوشتن کدهای await دار را در متد Main یک برنامه‌ی کنسول فراهم می‌کند.
- متد  File.Openدات نت، خروجی از نوع استریم دارد. برای تبدیل آن به نوع IRandomAccessStream، از متد الحاقی AsRandomAccessStream که پیشتر تهیه کردیم، می‌توان استفاده کرد.
- در ادامه متد PdfDocument.LoadFromStreamAsync این استریم خاص را دریافت کرده و امکان دسترسی به API ویندوز 8.1 را میسر می‌کند.
- توسط متد pdfDocument.GetPage می‌توان به صفحات مختلف فایل PDF باز شده دسترسی یافت. در اینجا متد page.RenderToStreamAsync، سبب رندر شدن صفحه با فرمت PNG می‌شود. این خروجی نهایتا باید در یک استریم از نوع IRandomAccessStream ثبت شود. در اینجا نیز می‌توان از متد File.Open در حالت FileMode.OpenOrCreate استفاده کرد.
- اگر می‌خواهید ابعاد تصویر نهایی و ویژگی‌های آن‌را تغییر دهید، می‌توان از پارامتر دوم متد page.RenderToStreamAsync استفاده کرد که شیءایی از نوع PdfPageRenderOptions را می‌پذیرد.


کدهای کامل این پروژه را از اینجا می‌توانید دریافت کنید
MicrosoftStreamExtensions.zip


برای مطالعه بیشتر
How to use specific WinRT API from Desktop apps
How to call WinRT APIs from .NET desktop apps
نظرات مطالب
آموزش MDX Query - قسمت ششم – شروع کار با دستورات MDX
در قسمت اول این مجموعه توضیحات لازم را در خصوص مقدمات کار و مفاهیم اولیه خدمتتان ارایه کردم. برای آشنایی با ساخت Data Warehouse لطفا مراجعه کنید به قسمت (مدل داده‌ای رابطه‌ای (Relational) وچند بعدی (Multidimensional) ) در سری اول.
در خصوص ایجاد ابعاد ی مانند بعد تاریخ و بعد سن باید عرض کنم که این ابعاد امکان ارایه انواع گزارشات مختلف را به شما خواهند داد و مطلقا OLAP را از حالت دینامیک خارج نمیکند.
در خصوص نحوه‌ی تحلیل و طراحی DW ، امیدوارم به زودی بتوانم مقاله ای را انتشار بدهم.
در خصوص عملگر‌های تقسیم و ... و توابع جمعی به زودی در قسمت‌های بعدی اطلاعات کاملی را خدمتتان ارایه خواهم کرد. به طور خلاصه اینکه OLAP برای ایجاد گزارشات مدیریتی می‌باشد و قطعا این موارد شما در ادامه پوشش  داده خواهد شد.(شما به راحتی می‌توانید سرجمع یک بازه‌ی تاریخی یا سنی را برای یک Measure خاص بدست آورید.)
مطالب
Reflection در ES6
در زبان‌های برنامه‌نویسی مانند سی‌شارپ و یا جاوا می‌توانیم از Reflection جهت خواندن متادیتاها استفاده کنیم. به عنوان مثال امکان تعریف پراپرتی و یا متدها و حتی تایپ‌هایی در زمان اجرا را در اختیارمان قرار می‌دهد. اما از آنجائیکه جاوا اسکریپت یک زبان داینامیک است، این قابلیت کمتر مورد توجه قرار گرفته است. در جاوا اسکریپت حین کار با کلاس‌ها و اشیاء، ممکن است نیاز داشته باشید تا از اعضای یک کلاس کوئری بگیرید و یا اینکه یک سری پراپرتی و متدهایی را در زمان اجرا به اشیاء‌تان اضافه کنید و یا مواردی از این دست.
تا قبل از ES 6 می‌توانستیم به این صورت به اعضای یک کلاس دسترسی داشته باشیم:
var person = {
    name: 'Sirwan',
    family: 'Afifi',
    doWork: function () {
        //....
    }
};

for (var prop in person) {
    console.log(prop); 
}
همانطور که مشاهده می‌کنید، توسط سینتکس for...in می‌توانیم به اعضای person دسترسی داشته باشیم. همچنین برای دسترسی به مقادیر هر کدام از اعضای آن می‌توانستیم از bracket syntax استفاده کنیم:
for (var prop in person) {
    console.log(person[prop]); 
}
لازم به ذکر است می‌توانستیم از متدهایی همانند Array.isArray, Object.getOwnPropertyDescriptor و حتی Object.keys نیز جهت دسترسی به اعضای پنهان یک شیء استفاده کنیم. اکنون قابلیت توکاری با نام Reflect این امکان را به ES 6 اضافه کرده است تا تمام اعمال فوق را به راحتی انجام داد.

Reflect
همانطور که عنوان شد توسط API جدیدی با نام Reflect، می‌توانیم به سادگی اعمال مربوط به Reflection را انجام دهیم. به عنوان مثال برای دسترسی به اعضای شیء person با استفاده از این API می‌توانیم به این صورت عمل کنیم:
var person = {
    name: 'Sirwan',
    family: 'Afifi',
    doWork: function () {
        //....
    }
};

for (let prop of Reflect.enumerate(person)) {
    console.log(`the value for ${prop} is ${person[prop]}`); 
  
}
در کد فوق از متد enumerate استفاده شده است، این متد یک پارامتر target را از ورودی می‌پذیرد. در واقع target همان شیءایی است که می‌خواهید پراپرتی‌های آن را پیمایش کنید. همچنین مقدار بازگشتی این متد یک iterator می‌باشد. مفهوم iterator نیز خیلی ساده است. به زبان ساده، امکان پیمایش درون یک کالکشن را در اختیارمان قرار می‌دهد. نکته‌ی دیگری که در کد فوق وجود دارد، استفاده از سینتکس for..of است، این سینتکس شبیه به for...in عمل می‌کند، با این تفاوت که در اینجا به جای پیمایش درون یکسری keys، درون values پیمایش می‌کنیم. در نتیجه برای پیمایش iterators باید از این سینتکس استفاده شود.

متدهای شیء Reflect
در ادامه تعدادی از متدهای شیء Reflect را مشاهده می‌کنید:
Reflect.get(target, name, [receiver])

Reflect.set(target, name, value, [receiver])

Reflect.has(target, name)

Reflect.apply(target, receiver, args)

Reflect.construct(target, args)

Reflect.getOwnPropertyDescriptor(target, name)

Reflect.defineProperty(target, name, desc)

Reflect.getPrototypeOf(target)

Reflect.setPrototypeOf(target, newProto)

Reflect.deleteProperty(target, name)

Reflect.enumerate(target)

Reflect.preventExtensions(target)

Reflect.isExtensible(target)

Reflect.ownKeys(target)
تعدادی از متدهای فوق خیلی مشابه متدهای Object هستند؛ با این تفاوت که متدهای فوق اطلاعات بهتری را بر می‌گردانند. به عنوان مثال متد Reflect.defineProperty در صورت ایجاد شدن یک پراپرتی جدید، مقدار true را برمی‌گرداند:
var yay = Reflect.defineProperty(target, 'foo', { value: 'bar' })
if (yay) {
  // yay!
} else {
  // oops.
}
اگر همین کار را با استفاده از متد Object.defineProperty انجام می‌دادیم می‌بایست کد را درون بلاک try/catch می‌نوشتیم:
try {
  Object.defineProperty(target, 'foo', { value: 'bar' })
  // yay!
} catch (e) {
  // oops.
}
به عنوان یک مثال دیگر، قبلاً برای حذف یک پراپرتی از یک شیء از کلمه کلیدی delete به اینصورت استفاده می‌کردیم:
var target = { foo: 'bar', baz: 'wat' }
delete target.foo
console.log(target)
// <- { baz: 'wat' }
اما با کمک متد Reflect.deleteProperty به راحتی می‌توانیم یک پراپرتی را حذف کنیم:
var target = { foo: 'bar', baz: 'wat' }
let isDeleted = Reflect.deleteProperty(target, 'foo')
console.log(isDeleted );
// true
در این‌حالت همانند متد Reflect.defineProperty، در صورت موفقیت‌آمیز بودن عمل حذف، مقدار true برگردانده می‌شود.
برای مشاهده‌ی لیست کامل متدهای فوق می‌توانید به اینجا مراجعه کنید. همچنین می‌توان با مراجعه به قسمت Browser compatibility وضعیت پشتیبانی هر کدام را در مرورگرهای مختلف مشاهده کرد.

نکاتی که در حین کار کردن با Reflect باید در نظر بگیرید:
  • این شیء فاقد متد [[Construct]] می‌باشد. یعنی نمی‌تواند همراه با کلمه‌ی کلیدی new مورد استفاده قرار گیرد.
  • همچنین نمی‌توان آن را همانند یک تابع فراخوانی کرد.
  • تمام متدهای آن به صورت استاتیک تعریف شده‌اند (همانند شیء Math).
مطالب
بررسی استراتژی‌های تشخیص تغییرات در برنامه‌های Angular
وقتی تغییری را در اشیاء خود به وجود می‌آورید، Angular بلافاصله متوجه آن‌ها شده و viewها را به روز رسانی می‌کند. هدف از این مکانیزم، اطمینان حاصل کردن از همگام بودن اشیاء مدل‌ها و viewها هستند. آگاهی از نحوه‌ی انجام این عملیات، کمک شایانی را به بالابردن کارآیی یک برنامه‌ی با رابط کاربری پیچیده‌ای می‌کند. یک شیء مدل در Angular عموما به سه طریق تغییر می‌کند:
- بروز رخ‌دادهای DOM مانند کلیک
- صدور درخواست‌های Ajax ایی
- استفاده از تایمرها (setTimer, setInterval)


ردیاب‌های تغییرات در Angular

تمام برنامه‌های Angular در حقیقت سلسله مراتبی از کامپوننت‌ها هستند. در زمان اجرای برنامه، Angular به ازای هر کامپوننت، یک تشخیص دهنده‌ی تغییرات را ایجاد می‌کند که در نهایت سلسله مراتب ردیاب‌ها را همانند سلسله مراتب کامپوننت‌ها ایجاد خواهد کرد. هر زمانیکه ردیابی فعال می‌شود، Angular این درخت را پیموده و مواردی را که تغییراتی را گزارش داده‌اند، بررسی می‌کند. این پیمایش به ازای هر تغییر رخ داده‌ی در مدل‌های برنامه صورت می‌گیرد و همواره از بالای درخت شروع شده و به صورت ترتیبی تا پایین آن ادامه پیدا می‌کند:


این پیمایش ترتیبی از بالا به پایین، از این جهت صورت می‌گیرد که اطلاعات کامپوننت‌ها از والدین آن‌ها تامین می‌شوند. تشخیص دهنده‌های تغییرات، روشی را جهت ردیابی وضعیت پیشین و فعلی یک کامپوننت ارائه می‌دهد تا Angular بتواند تغییرات رخ‌داده را منعکس کند. اگر Angular گزارش تغییری را از یک تشخیص دهنده‌ی تغییر دریافت کند، کامپوننت مرتبط را مجددا ترسیم کرده و DOM را به روز رسانی می‌کند.


استراتژی‌های تشخیص تغییرات در Angular

برای درک نحوه‌ی عملکرد سیستم تشخیص تغییرات نیاز است با مفهوم value types و reference types در JavaScript آشنا شویم. در JavaScript نوع‌های زیر value type هستند:
• Boolean
• Null
• Undefined
• Number
• String
و نوع‌های زیر Reference type محسوب می‌شوند:
• Arrays
• Objects
• Functions
مهم‌ترین تفاوت بین این دو نوع، این است که برای دریافت مقدار یک value type فقط کافی است از stack memory کوئری بگیریم. اما برای دریافت مقادیر reference types باید ابتدا در جهت یافتن شماره ارجاع آن، از stack memory کوئری گرفته و سپس بر اساس این شماره ارجاع، اصل مقدار آن‌را در heap memory پیدا کنیم.
این دو تفاوت را می‌توان در شکل زیر بهتر مشاهده کرد:



استراتژی Default یا پیش‌فرض تشخیص تغییرات در Angular

همانطور که پیشتر نیز عنوان شد، Angular تغییرات یک شیء مدل را در جهت تشخیص تغییرات و انعکاس آن‌ها به View برنامه، ردیابی می‌کند. در این حالت هر تغییری بین حالت فعلی و پیشین یک شیء مدل برای این منظور بررسی می‌گردد. در اینجا Angular این سؤال را مطرح می‌کند: آیا مقداری در این مدل تغییر یافته‌است؟
اما برای یک reference type می‌توان سؤالات بهتری را مطرح کرد که بهینه‌تر و سریعتر باشند. اینجاست که استراتژی OnPush تشخیص تغییرات مطرح می‌شود.


استراتژی OnPush تشخیص تغییرات در Angular

ایده اصلی استراتژی OnPush تشخیص تغییرات در Angular در immutable فرض کردن reference types نهفته‌است. در این حالت هر تغییری در شیء مدل، سبب ایجاد یک ارجاع جدید به آن در stack memory می‌شود. به این ترتیب می‌توان تشخیص تغییرات بسیار سریعتری را شاهد بود. چون دیگر در اینجا نیازی نیست تمام مقادیر یک شیء را مدام تحت نظر قرار داد. همینقدر که ارجاع آن در stack memory تغییر کند، یعنی مقادیر این شیء در heap memory تغییر یافته‌اند.
در این حالت Angular دو سؤال را مطرح می‌کند: آیا ارجاع به یک reference type در stack memory تغییر یافته‌است؟ اگر بله، آیا مقادیر آن در heap memory تغییر کرده‌اند؟ برای مثال جهت بررسی تغییرات یک آرایه‌ی با 30 عضو، دیگر در ابتدای کار نیازی نیست تا هر 30 عضو آن بررسی شوند (برخلاف حالت پیش‌فرض بررسی تغییرات). در حالت استراتژی OnPush، ابتدا مقدار ارجاع این آرایه در stack memory بررسی می‌شود. اگر تغییری در آن صورت گرفته بود، به معنای تغییری در اعضای آرایه‌است.
استراتژی OnPush در یک کامپوننت به نحو ذیل فعال و انتخاب می‌شود و مقدار پیش‌فرض آن ChangeDetectionStrategy.Default است:
 import {ChangeDetectionStrategy, Component} from '@angular/core';
@Component({
  // ...
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class OnPushComponent {
  // ...
}
استراتژی OnPush تغییرات یک کامپوننت را در حالت‌های ذیل نیز ردیابی می‌کند:
- اگر مقدار یک خاصیت از نوع Input@ تغییر کند.
- اگر یک event handler رخ‌دادی را صادر کند.
- اگر سیستم ردیابی به صورت دستی فراخوانی شود.
- اگر سیستم ردیاب تغییرات child component آن، اجرا شود.


نوع‌های ارجاعی Immutable

همانطور که عنوان شد، شرط کار کردن استراتژی OnPush، داشتن نوع‌های ارجاعی immutable است. اما نوع ارجاعی immutable چیست؟
Immutable بودن به زبان ساده به این معنا است که ما هیچگاه جهت تغییر مقدار خاصیتی در یک شیء، آن خاصیت را مستقیما مقدار دهی نمی‌کنیم؛ بلکه کل شیء را مجددا مقدار دهی می‌کنیم.
برای نمونه در مثال زیر، خاصیت foo شیء before مستقیما مقدار دهی شده‌است:
static mutable() {
  var before = {foo: "bar"};
  var current = before;
  current.foo = "hello";
  console.log(before === current);
  // => true
}
اما اگر بخواهیم با آن به صورت Immutable «رفتار» کنیم، کل این شیء را جهت اعمال تغییرات، مقدار دهی مجدد خواهیم کرد:
static immutable() {
  var before = {foo: "bar"};
  var current = before;
  current = {foo: "hello"};
  console.log(before === current);
  // => false
}
البته باید دقت داشت در هر دو مثال، شیء‌های ایجاد شده در اصل mutable هستند؛ اما در مثال دوم، با این شیء به صورت immutable «رفتار» شده‌است و صرفا «تظاهر» به رفتار immutable با یک شیء ارجاعی صورت گرفته‌است.


معرفی کتابخانه‌ی Immutable.js

جهت ایجاد اشیاء واقعی immutable کتابخانه‌ی Immutable.js توسط Facebook ایجاد شده‌است و برای کار با استراتژی تشخیص تغییرات OnPush در Angular بسیار مناسب است.
برای نصب آن دستور ذیل  را صادر نمائید:
npm install immutable --save
یک نمونه مثال از کاربرد ساختارهای داده‌ی List و Map آن برای کار با آرایه‌ها و اشیاء:
import {Map, List} from 'immutable';

var foobar = {foo: "bar"};
var immutableFoobar = Map(foobar);
console.log(immutableFooter.get("foo"));
// => bar

var helloWorld = ["Hello", "World!"];
var immutableHelloWorld = List(helloWorld);
console.log(immutableHelloWorld.first());
// => Hello
console.log(immutableHelloWorld.last());
// => World!
helloWorld.push("Hello Mars!");
console.log(immutableHelloWorld.last());
// => Hello Mars!


تغییر ارجاع به یک شیء با استفاده از spread properties

const user = {
  name: 'Max',
  age: 30
}
user.age = 31
در این مثال تنها خاصیت age شیء user به روز رسانی می‌شود. بنابراین ارجاع به این شیء تغییر نخواهد کرد و اگر از روش changeDetection: ChangeDetectionStrategy.OnPush استفاده کنیم، رابط کاربری برنامه به روز رسانی نخواهد شد و این تغییر صرفا با بررسی عمیق تک تک خواص این شیء با وضعیت قبلی آن قابل تشخیص است (یا همان حالت پیش فرض بررسی تغییرات در Angular و نه حالت OnPush).
اگر علاقمند به استفاده‌ی از یک کتابخانه‌ی اضافی مانند Immutable.js در کدهای خود نباشید، روش دیگری نیز برای تغییر ارجاع به یک شیء وجود دارد:
const user = {
  name: 'Max',
  age: 30
}
const modifiedUser = { ...user, age: 31 }
در اینجا با استفاده از spread properties یک شیء کاملا جدید ایجاد شده‌است و ارجاع به آن با ارجاع به شیء user یکی نیست.

نمونه‌ی دیگر آن در حین کار با متد push یک آرایه‌است:
export class AppComponent {
  foods = ['Bacon', 'Lettuce', 'Tomatoes'];
 
  addFood(food) {
    this.foods.push(food);
  }
}
متد push، بدون تغییر ارجاعی به آرایه‌ی اصلی، عضوی را به آن آرایه اضافه می‌کند. بنابراین اعضای اضافه شده‌ی به آن نیز توسط استراتژی OnPush قابل تشخیص نیستند. اما اگر بجای متد push از spread operator استفاده کنیم:
addFood(food) {
  this.foods = [...this.foods, food];
}
اینبار this.food به یک شیء کاملا جدید اشاره می‌کند که ارجاع به آن، با ارجاع به شیء this.food قبلی یکی نیست. بنابراین استراتژی OnPush قابلیت تشخیص تغییرات آن‌را دارد.


آگاه سازی دستی موتور تشخیص تغییرات Angular در حالت استفاده‌ی از استراتژی OnPush

تا اینجا دریافتیم که استراتژی OnPush تنها به تغییرات ارجاعات به اشیاء پاسخ می‌دهد و به نحوی باید این ارجاع را با هر به روز رسانی تغییر داد. اما روش دیگری نیز برای وادار کردن این سیستم به تغییر وجود دارد:
 import { Component,  Input, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
 
@Component({
  selector: 'app-child',
  templateUrl: './child.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChildComponent {
  @Input() data: string[];
 
  constructor(private cd: ChangeDetectorRef) {}
 
  refresh() {
      this.cd.detectChanges();
  }
}
در این کامپوننت از استراتژی OnPush استفاده شده‌است. در اینجا می‌توان همانند قبل با اشیاء و آرایه‌های موجود کار کرد (بدون اینکه ارجاعات به آن‌ها را تغییر دهیم و یا آن‌ها را immutable کنیم) و در پایان کار، متد detectChanges سرویس ChangeDetectorRef را به صورت دستی فراخوانی کرد تا Angular کار رندر مجدد view این کامپوننت را بر اساس تغییرات آن انجام دهد (کل کامپوننت به عنوان یک کامپوننت تغییر کرده به سیستم ردیابی تغییرات معرفی می‌شود).


کار با Observables در حالت استفاده‌ی از استراتژی OnPush

مطلب «صدور رخدادها از سرویس‌ها به کامپوننت‌ها در برنامه‌های Angular» را در نظر بگیرید. Observables نیز ما را از تغییرات رخ‌داده آگاه می‌کنند؛ اما برخلاف immutable objects با هر تغییری که رخ می‌دهد، ارجاع به آن‌ها تغییری نمی‌کند. آن‌ها تنها رخ‌دادی را به مشترکین، جهت اطلاع رسانی از تغییرات صادر می‌کنند.
بنابراین اگر از Observables و استراتژی OnPush استفاده کنیم، چون ارجاع به آن‌ها تغییری نمی‌کند، رخ‌دادهای صادر شده‌ی توسط آن‌ها ردیابی نخواهند شد. برای رفع این مشکل، امکان علامتگذاری رخ‌دادهای Observables به تغییر کرده پیش‌بینی شده‌است.
در اینجا کامپوننتی را داریم که قابلیت صدور رخ‌دادها را از طریق یک BehaviorSubject دارد:
 import { Component } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
 
@Component({ ... })
export class AppComponent {
  foods = new BehaviorSubject(['Bacon', 'Letuce', 'Tomatoes']);
 
  addFood(food) {
     this.foods.next(food);
  }
}
و کامپوننت دیگری توسط خاصیت ورودی data از نوع Observable در متد ngOnInit، مشترک آن خواهد شد:
 import { Component, Input, ChangeDetectionStrategy, ChangeDetectorRef, OnInit } from '@angular/core';
import { Observable } from 'rxjs/Observable';
 
@Component({
  selector: 'app-child',
  templateUrl: './child.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChildComponent implements OnInit {
  @Input() data: Observable<any>;
  foods: string[] = [];
 
  constructor(private cd: ChangeDetectorRef) {}
 
  ngOnInit() {
     this.data.subscribe(food => {
        this.foods = [...this.foods, ...food];
     });
  }
در این حالت چون از ChangeDetectionStrategy.OnPush استفاده می‌شود و ارجاع به this.data این observable با هر بار صدور رخ‌دادی توسط آن، تغییر نمی‌کند، سیستم ردیابی تغییرات آن‌را به عنوان تغییر کرده درنظر نمی‌گیرد. برای رفع این مشکل تنها کافی است رخ‌دادگردان آن‌را با متد markForCheck علامتگذاری کنیم:
ngOnInit() {
  this.data.subscribe(food => {
      this.foods = [...this.foods, ...food];
      this.cd.markForCheck(); // marks path
   });
}
markForCheck به Angular اعلام می‌کند که این مسیر ویژه را در بررسی بعدی سیستم ردیابی تغییرات لحاظ کن.
مطالب دوره‌ها
آشنایی با نحوه ایجاد یک IoC Container
قبل از اینکه وارد بحث استفاده از کتابخانه‌های بسیار غنی IoC Container موجود شویم، بهتر است یک نمونه ساده آن‌ها را طراحی کنیم تا بهتر بتوان با عملکرد و ساختار درونی آن‌ها آشنا شد.


IoC Container چیست؟

IoC Container، فریم ورکی است برای انجام تزریق وابستگی‌ها. در این فریم ورک امکان تنظیم اولیه وابستگی‌های سیستم وجود دارد. برای مثال زمانیکه برنامه از یک IoC Container، نوع اینترفیس خاصی را درخواست می‌کند، این فریم ورک با توجه به تنظیمات اولیه‌اش، کلاسی مشخص را بازگشت خواهد داد.
IoC Containerهای قدیمی‌تر، برای انجام تنظیمات اولیه خود از فایل‌های کانفیگ استفاده می‌کردند. نمونه‌های جدیدتر آن‌ها از روش‌های Fluent interfaces برای مشخص سازی تنظیمات خود بهره می‌برند.

زمانیکه از یک IOC Container در کدهای خود استفاده می‌کنید، مراحلی چند رخ خواهند داد:
الف) کد فراخوان، از IOC Container، یک شیء مشخص را درخواست می‌کند. عموما اینکار با درخواست یک اینترفیس صورت می‌گیرد؛ هرچند محدودیتی نیز نداشته و امکان درخواست یک کلاس از نوعی مشخص نیز وجود دارد.
ب) در ادامه IOC Container به لیست اشیاء قابل ارائه توسط خود نگاه کرده و در صورت وجود، وهله سازی شیء درخواست شده را انجام و نهایتا شیء مطلوب را بازگشت خواهد داد.
در این بین زنجیره‌ی وابستگی‌های مورد نیاز نیز وهله سازی خواهند شد. برای مثال اگر وابستگی اول به وابستگی دوم برای وهله سازی نیاز دارد، کار وهله سازی وابستگی‌های وابستگی دوم نیز به صورت خودکار انجام خواهند شد. (این موردی است که بسیاری از تازه واردان به این بحث تا یکبار آن‌را امتحان نکنند باور نخواهند کرد!)
ج) سپس کد فراخوان وهله دریافتی را مورد پردازش قرار داده و سپس شروع به استفاده از متدها و خواص آن خواهد نمود.


در تصویر فوق محل قرارگیری یک IOC Container را مشاهده می‌کنید. یک IOC Container در مورد تمام وابستگی‌های مورد نیاز، اطلاعات لازم را دارد. همچنین این فریم ورک در مورد کلاسی که قرار است از وابستگی‌های سیستم استفاده نماید نیز مطلع است؛ به این ترتیب می‌تواند به صورت خودکار در زمان وهله سازی آن، نوع‌های وابستگی‌های مورد نیاز آن‌را در اختیارش قرار دهد.
برای مثال در اینجا MyClass، وابستگی مشخص شده در سازنده خود را به نام IDependency از IOC Container درخواست می‌کند. سپس این IOC Container بر اساس تنظیمات اولیه خود، یکی از وابستگی‌های A یا B را بازگشت خواهد داد.


آغاز به کار ساخت یک IOC Container نمونه

در ابتدا کدهای آغازین مثال بحث جاری را در نظر بگیرید:
using System;

namespace DI01
{
    public interface ICreditCard
    {
        string Charge();
    }

    public class Visa : ICreditCard
    {
        public string Charge()
        {
            return "Charging with the Visa!";
        }
    }

    public class MasterCard : ICreditCard
    {
        public string Charge()
        {
            return "Swiping the MasterCard!";
        }
    }

    public class Shopper
    {
        private readonly ICreditCard creditCard;

        public Shopper(ICreditCard creditCard)
        {
            this.creditCard = creditCard;
        }

        public void Charge()
        {
            var chargeMessage = creditCard.Charge();
            Console.WriteLine(chargeMessage);
        }
    }    
}
در اینجا وابستگی‌های کلاس خریدار از طریق سازنده آن که متداول‌ترین روش تزریق وابستگی‌ها است، در اختیار آن قرار خواهد گرفت. یک اینترفیس کردیت کارت تعریف شده‌است به همراه دو پیاده سازی نمونه آن مانند مسترکارت و ویزا کارت. ساده‌ترین نوع فراخوانی آن نیز می‌تواند مانند کدهای ذیل باشد (تزریق وابستگی‌های دستی):
 var shopper = new Shopper(new Visa());
shopper.Charge();
در ادامه قصد داریم این فراخوانی‌ها را اندکی هوشمندتر کنیم تا بتوان بر اساس تنظیمات برنامه، کار تزریق وابستگی‌ها صورت گیرد و به سادگی بتوان اینترفیس‌های متفاوتی را در اینجا درخواست و مورد استفاده قرار داد. اینجا است که به اولین IoC Container خود خواهیم رسید:
using System;
using System.Collections.Generic;
using System.Linq;

namespace DI01
{
    public class Resolver
    {
        //کار ذخیره سازی و نگاشت از یک نوع به نوعی دیگر در اینجا توسط این دیکشنری انجام خواهد شد
        private Dictionary<Type, Type> dependencyMap = new Dictionary<Type, Type>();

        /// <summary>
        /// یک نوع خاص از آن درخواست شده و سپس بر اساس تنظیمات برنامه، کار وهله سازی
        /// نمونه معادل آن صورت خواهد گرفت
        /// </summary>
        public T Resolve<T>()
        {
            return (T)Resolve(typeof(T));
        }

        private object Resolve(Type typeToResolve)
        {
            Type resolvedType;

            // ابتدا بررسی می‌شود که آیا در تنظیمات برنامه نگاشت متناظری برای نوع درخواستی وجود دارد؟
            if (!dependencyMap.TryGetValue(typeToResolve, out resolvedType))
            {
                //اگر خیر، کار متوقف خواهد شد
                throw new Exception(string.Format("Could not resolve type {0}", typeToResolve.FullName));
            }

            var firstConstructor = resolvedType.GetConstructors().First();
            var constructorParameters = firstConstructor.GetParameters();
            // در ادامه اگر این نوع، دارای سازنده‌ی بدون پارامتری است
            // بلافاصله وهله سازی خواهد شد
            if (!constructorParameters.Any())
                return Activator.CreateInstance(resolvedType);


            var parameters = new List<object>();
            foreach (var parameterToResolve in constructorParameters)
            {
                // در اینجا یک فراخوانی بازگشتی صورت گرفته است برای وهله سازی
                // خودکار پارامترهای مختلف سازنده یک کلاس
                parameters.Add(Resolve(parameterToResolve.ParameterType));
            }            
            return firstConstructor.Invoke(parameters.ToArray());
        }

        public void Register<TFrom, TTo>()
        {
            dependencyMap.Add(typeof(TFrom), typeof(TTo));
        }
    }
}
در اینجا کدهای کلاس Resolver یا همان IoC Container ابتدایی بحث را مشاهده می‌کنید. توضیحات قسمت‌های مختلف آن به صورت کامنت ارائه شده‌اند.
 var resolver = new Resolver();
//تنظیمات اولیه
resolver.Register<Shopper, Shopper>();
resolver.Register<ICreditCard, Visa>();
//تزریق وابستگی‌ها و وهله سازی
var shopper = resolver.Resolve<Shopper>();
shopper.Charge();
در ادامه نحوه استفاده از IoC Container ایجاد شده را مشاهده می‌کنید.
ابتدا کار تعاریف نگاشت‌های اولیه انجام می‌شود. در این صورت زمانیکه متد Resolve فراخوانی می‌گردد، نوع درخواستی آن به همراه سازنده دارای آرگومانی از نوع ICreditCard وهله سازی شده و بازگشت داده خواهد شد. سپس با در دست داشتن یک وهله آماده، متد Charge آن‌را فراخوانی خواهیم کرد.


بررسی نحوه استفاده از Microsoft Unity به عنوان یک IoC Container

Unity چیست؟

Unity یک فریم ورک IoC Container تهیه شده توسط مایکروسافت می‌باشد که آن‌را به عنوان جزئی از Enterprise Library خود قرار داده است. بنابراین برای دریافت آن یا می‌توان کل مجموعه Enterprise Library را دریافت کرد و یا به صورت مجزا به عنوان یک بسته نیوگت نیز قابل تهیه است.
برای این منظور در خط فرمان پاورشل نیوگت در VS.NET دستور ذیل را اجرا کنید:
 PM> Install-Package Unity

پیاده سازی مثال خریدار توسط Unity

همان مثال قسمت قبل را درنظر بگیرید. قصد داریم اینبار بجای IoC Container دست سازی که تهیه شد، پیاده سازی آن‌را به کمک MS Unity انجام دهیم.
using Microsoft.Practices.Unity;

namespace DI02
{
    class Program
    {
        static void Main(string[] args)
        {
            var container = new UnityContainer();

            container.RegisterType<ICreditCard, MasterCard>();

            var shopper = container.Resolve<Shopper>();
            shopper.Charge();
        }
    }
}
همانطور که ملاحظه می‌کنید، API آن بسیار شبیه به کلاس دست سازی است که در قسمت قبل تهیه کردیم.
مطابق کدهای فوق، ابتدا تنظیمات IoC Container انجام شده است. به آن اعلام کرده‌ایم که در صورت نیاز به ICreditCard، نوع MasterCard را یافته و وهله سازی کن. با این تفاوت که Unity هوشمند‌تر بوده و سطر مربوط به ثبت کلاس Shoper ایی را که در قسمت قبل انجام دادیم، در اینجا حذف شده است.
سپس به این IoC Container اعلام کرده‌ایم که نیاز به یک وهله از کلاس خریدار داریم. در اینجا Unity کار وهله سازی‌های خودکار وابستگی‌ها و تزریق آن‌ها را در سازنده کلاس خریدار انجام داده و نهایتا یک وهله قابل استفاده را در اختیار ادامه برنامه قرار خواهد داد.

یک نکته:
به صورت پیش فرض کار تزریق وابستگی‌ها در سازنده کلاس‌ها به صورت خودکار انجام می‌شود. اگر نیاز به Setter injection و مقدار دهی خواص کلاس وجود داشت می‌توان به نحو ذیل عمل کرد:
 container.RegisterType<ICreditCard, MasterCard>(new InjectionProperty("propertyName", 5));
نام خاصیت و مقدار مورد نظر به عنوان پارامتر متد RegisterType باید تعریف شوند.


مدیریت طول عمر اشیاء در Unity

توسط یک IoC Container می‌توان یک وهله معمولی از شیءایی را درخواست کرد و یا حتی طول عمر این وهله را به صورت Singleton معرفی نمود (یک وهله در طول عمر کل برنامه). در Unity اگر تنظیم خاصی اعمال نشود، هربار که متد Resolve فراخوانی می‌گردد، یک وهله جدید را در اختیار ما قرار خواهد داد. اما اگر پارامتر متد RegisterType را با وهله‌ای از ContainerControlledLifetimeManager مقدار دهی کنیم:
 container.RegisterType<ICreditCard, MasterCard>(new ContainerControlledLifetimeManager());
از این پس با هربار فراخوانی متد Resolve، در صورت نیاز به وابستگی از نوع ICreditCard، تنها یک وهله مشترک از MasterCard ارائه خواهد شد.
حالت پیش فرض مورد استفاده، بدون ذکر پارامتر متد RegisterType، مقدار TransientLifetimeManager می‌باشد.
مطالب
LINQ to JSON به کمک JSON.NET
عموما از امکانات LINQ to JSON کتابخانه‌ی JSON.NET زمانی استفاده می‌شود که ورودی JSON تو در توی حجیمی را دریافت کرده‌اید اما قصد ندارید به ازای تمام موجودیت‌های آن یک کلاس معادل را جهت نگاشت به آن‌ها تهیه کنید و صرفا یک یا چند مقدار تو در توی آن جهت عملیات استخراج نهایی مدنظر است. به علاوه در اینجا LINQ to JSON واژه‌ی کلیدی dynamic را نیز پشتیبانی می‌کند.


همانطور که در تصویر مشخص است، خروجی‌های JSON عموما ترکیبی هستند از مقادیر، آرایه‌ها و اشیاء. هر کدام از این‌ها در LINQ to JSON به اشیاء JValue، JArray و JObject نگاشت می‌شوند. البته در حالت JObject هر عضو به یک JProperty و JValue تجزیه خواهد شد.
برای مثال آرایه [1,2] تشکیل شده‌است از یک JArray به همراه دو JValue که مقادیر آن‌را تشکیل می‌دهند. اگر مستقیما بخواهیم یک JArray را تشکیل دهیم می‌توان از شیء JArray استفاده کرد:
 var array = new JArray(1, 2, 3);
var arrayToJson = array.ToString();
و اگر یک JSON رشته‌ای دریافتی را داریم می‌توان از متد Parse مربوط به JArray کمک گرفت:
 var json = "[1,2,3]";
var jArray= JArray.Parse(json);
var val = (int)jArray[0];
خروجی JArray یک لیست از JTokenها است و با آن می‌توان مانند لیست‌های معمولی کار کرد.

در حالت کار با اشیاء، شیء JObject امکان تهیه اشیاء JSON ایی را دارا است که می‌تواند مجموعه‌ای از JPropertyها باشد:
 var jObject = new JObject(
new JProperty("prop1", "value1"),
new JProperty("prop2", "value2")
);
var jObjectToJson = jObject.ToString();
با JObject به صورت dynamic نیز می‌توان کار کرد:
 dynamic jObj = new JObject();
jObj.Prop1 = "value1";
jObj.Prop2 = "value2";
jObj.Roles = new[] {"Admin", "User"};
این روش بسیار شبیه است به حالتی که با اشیاء جاوا اسکریپتی در سمت کلاینت می‌توان کار کرد.
و حالت عکس آن توسط متد JObject.Parse قابل انجام است:
 var json = "{ 'prop1': 'value1', 'prop2': 'value2'}";
var jObj = JObject.Parse(json);
var val1 = (string)jObj["prop1"];

اکنون که با اجزای تشکیل دهنده‌ی LINQ to JSON آشنا شدیم، مثال ذیل را درنظر بگیرید:
 var array = @"[
{
  'prop1': 'value1',
  'prop2': 'value2'
},
{
  'prop1': 'test1',
  'prop2': 'test2'
}
]";
var objects = JArray.Parse(array);
var obj1 = objects.FirstOrDefault(token => (string) token["prop1"] == "value1");
خروجی JArray یا JObject از نوع IEnumerable است و بر روی آن‌ها می‌توان کلیه متدهای LINQ را فراخوانی کرد. برای مثال در اینجا اولین شیءایی که مقدار خاصیت prop1 آن مساوی value1 است، یافت می‌شود و یا می‌توان اشیاء را بر اساس مقدار خاصیتی مرتب کرده و سپس آن‌‌ها را بازگشت داد:
 var values = objects.OrderBy(token => (string) token["prop1"])
.Select(token => new {Value = (string) token["prop2"]})
.ToList();
امکان انجام sub queries نیز در اینجا پیش بینی شده‌است:
 var array = @"[
{
  'prop1': 'value1',
  'prop2': [1,2]
},
{
  'prop1': 'test1',
  'prop2': [1,2,3]
}
]";
var objects = JArray.Parse(array);
var objectContaining3 = objects.Where(token => token["prop2"].Any(v => (int)v == 3)).ToList();
در این مثال، خواص prop2 از نوع آرایه‌ای از اعداد صحیح هستند. با کوئری نوشته شده، اشیایی که خاصیت prop2 آن‌ها دارای عضو 3 است، یافت می‌شوند.
مطالب
مروری بر کتابخانه ReactJS - قسمت اول - آشنایی با ReactJS
در این سری مقالات، مروری بر کتابخانه ReactJS خواهیم داشت. به طور کلی با آن آشنا می‌شویم، برای Visual Studio Code پیکربندیش میکنیم و قابلیت‌های مختلف کتابخانه را بررسی میکنیم. هر چند که مثالها در کل ساده هستند، اما پیش نیاز درک کامل آنها، آشنا بودن خواننده با HTML DOM، JavaScript و  Ajax است. در قسمت اول، کتابخانه را معرفی و مثال‌هایی از امکانات اصلی آن‌را مرور میکنیم.  

React یک کتابخانه متن‌باز جاوااسکریپتی، برای ساخت رابط کاربری به صورت پویا، بر پایه تغییر وضعیت اولیه المانها (تگ‌ها) نسبت به داده‌های وارد شده از سمت سرور یا داده‌های ایجاد شده در سمت کاربر، برای ساخت برنامه‌های تک‌صفحه‌ای در بستر وب است. این کتابخانه توسط فیس بوک ساخته شده و توسط فیس‌بوک، اینستاگرام و جمعی از شرکت‌ها و اشخاص منفرد، توسعه داده شده و نگهداری میشود. 
کلمه React به معنای واکنش نشان دادن است و این دقیقا کاری است که این کتابخانه انجام میدهد. وقتی بخشی از برنامه تغییر می‌کند، این تغییرات باید در جایی منعکس شوند. مثلا اگر توسط Ajax داده‌هایی را از سرور دریافت کرده‌ایم، به چیزی بیشتر از یک جدول ثابت برای نمایش و تبادل با داده‌های رسیده احتیاج داریم. توسط React رابط کاربری (HTML) را با استفاده از JavaScript ایجاد میکنیم. React برای کار با Ajax فوق‌العاده است! 
مرورگر‌ها برای رندر کردن یک HTML DOM به صورت پویا مشکلی ندارند؛ اما به اندازه کافی سریع نیستند. بخصوص زمانیکه نیاز به به‌روز کردن DOM می‌رسد و مرورگر تغییرات جدید را در حافظه موقت خود ندارد. DOM یک گلوگاه است و بهتر است، از داشتن کدهای خیلی زیاد HTML در صفحه پرهیز کنیم. بخصوص در صفحه‌هایی با اطلاعات پویا بهتر است کار ساخت و تغییر رابط کاربری را به JavaScript بسپاریم. اگر تگ‌های HTML به صورت اشیاء JavaScript ارائه شوند، امکانات بیشتری برای کار با آنها خواهیم داشت. 
React متد createElement را برای ساخت تگ‌های HTML دارد که یک شیء JavaScript را ایجاد میکند. البته می‌شود همین کار را با JavaScript نیز انجام داد. ارزش ایجاد تگ‌های HTML با React زمانی است که میخواهیم  با داده‌ها و تغییرات آنها سر و کار داشته باشیم. در قطعه کد زیر ساخت تگ img، توسط JavaScript و React آورده شده. 
var image = document.createElement("img");
image.setAttribute("src", "logo.png");

React.createElement("img", { src : "logo.png" });
با ساخت تگ‌ها توسط React، نماینده‌ای از تگ ساخته شده را در حافظه داریم که از نمونه‌ای که در مرورگر به صورت ایستا وجود دارد، جداست. به این صورت می‌توانیم تغییراتی را که میخواهیم بر روی DOM انجام شوند، بر اساس ساختاری که در حافظه داریم، اعمال کنیم.  

Virtual DOM

تفاوت در ساخت تگ‌های HTML به صورت مجازی بین JavaScript و React این است که React وضعیت تگ‌هایی را که می‌سازد دنبال می‌کند. برای مثال فرض کنید نام سه محصول را در یک تگ < ul > نشان داده‌ایم. React وضعیت اصلی این تگ را که به مرورگر فرستاده، در حافظه دارد و همچنین در اثر تغییر منبع داده‌ای که برای < ul > مشخص کرده‌ایم (که میتواند ورود اطلاعات به صورت Ajax باشد (مثلا اضافه شدن یک محصول جدید)) وضعیت جدیدی را برای تگ < ul > در حافظه ایجاد میکند. با وجود دو وضعیت برای یک تگ در حافظه، React میتواند تفاوت بین آنها را تشخیص داده و تگ را به روز کند. به این حالت عملکرد React ، اصطلاحا Virtual DOM می‌گویند.

React رابط کاربری را به صورت یک مدل می‌بیند و این مدل را با توجه به وضعیت اصلی آن در حافظه دوباره می‌سازد. برای React مهم نیست که ماهیت تغییر چیست. فقط وضعیت‌ها را مثل دو عکس می‌بیند و میفهمد که آیا چیزی عوض شده‌است یا نه. دیالوگ React با مرورگر اینطور است: ای تگ < ul > این لیست را نشان بده (لیستی با سه محصول)، و بعد می‌گوید: ای تگ < ul > این لیست را نشان بده (لیستی با چهار محصول)!


کامپوننت‌های React

رابط‌های کاربری مثل تگ‌های HTML  برای React به معنای Component هستند. استفاده از این مؤلفه‌های مجزا، مزایای زیادی دارند که در زیر مثالی از نحوه ساخت یک Component را در React می‌بینیم.   
<a href = “http://google.com”>
     <img src=”google.png”/>
</a>

// Components
<clickableimage/>
<linkimage/>

در کد بالا، بخش اول واضح است. عکسی که قابلیت کلیک شدن را دارد. حال فرض کنید یکی از کامپوننت‌های  <clickableimage/> یا <linkimage/>، همان تصویر قابل کلیک را ایجاد کنند. با نام گذاری واضح کامپوننت‌ها، خوانایی برنامه بهتر می‌شود. یعنی میدانیم هر کامپوننت چه کاری را برای ما انجام میدهد. با این تصور که اگر تگ‌های زیاد و طولانی را در بخش رابط کاربری داریم، ارزش استفاده از کامپوننت‌های  React مشخص می‌شود.


قابلیت استفاده مجدد

در React کامپوننت‌ها برای اساس توابع ساخته می‌شوند. یعنی وقتی یک کامپوننت را صدا بزنیم، در واقع یک تابع را اجرا می‌کنیم. در نتیجه کامپوننت‌ها رفتار توابع را دارند؛ ورودی میگیرند و خروجی که یک DOM مجازی است را تحویل میدهند. اگر تابعی که مسئول ساخت کامپوننت است وابستگی به توابع یا متغیرهای بیرونی نداشته باشد، میتواند در جای دیگری از برنامه یا برنامه‌ای دیگر مجددا استفاده شود. کد زیر نشان میدهد که چطور کامپوننت‌های React ساخته می‌شوند.  
var ClickableImage = function(props) {
  return (
      <a href={props.href}>
         <img src={props.src} />
      </a>
    );
};

ReactDOM.render(
<ClickableImage href="http://google.com" src="logo.png" />,
document.getElementById("targetDivId"));
در قسمت‌های بعد، به هر یک از امکانات ReactJS نگاهی دقیق‌تر و مثال‌هایی بیشتر، خواهیم داشت.  
مطالب
نگاهی به Latent Semantic Indexing
مقدمه ای بر Latent Semantic Indexing

هنگامیکه برای اولین بار، جستجو بر مبنای کلمات کلیدی (keyword search) بر روی مجموعه‌ای از متون، به دنیای بازیابی اطلاعات معرفی شد شاید فقط یک ذهنیت مطرح می‌شد و آن یافتن لغت در متن بود. به بیان دیگر در آن زمان تنها بدنبال متونی می‌گشتیم که دقیقا شامل کلمه کلیدی مورد جستجوی کاربر باشند. روال کار نیز بدین صورت بود که از دل پرس و جوی کاربر، کلماتی بعنوان کلمات کلیدی استخراج می‌شد. سپس الگوریتم جستجو در میان متون موجود بدنبال متونی می‌گشت که دقیقا یک یا تمامی کلمات کلیدی در آن آمده باشند. اگر متنی شامل این کلمات بود به مجموعه جواب‌ها اضافه می‌گردید و در غیر این صورت حذف می‌گشت. در پایان جستجو با استفاده از الگوریتمی، نتایج حاصل رتبه بندی می‌گشت و به ترتیب رتبه با کاربر نمایش داده می‌شد.
نکته مهمی که در این روش دیده می‌شود اینست که متون به تنهایی و بدون در نظر گرفتن کل مجموعه پردازش می‌شدند و اگر تصمیمی مبنی بر جواب بودن یک متن گرفته می‌شد، آن تصمیم کاملا متکی به همان متن و مستقل از متون دیگر گرفته می‌شد. در آن سال‌ها هیچ توجهی به وابستگی موجود بین متون مختلف و ارتباط بین آنها  نمی‌شد که این مسئله یکی از عوامل پایین بودن دقت جستجو‌ها بشمار می‌رفت.
در ابتدا بر اساس همین دیدگاه  الگوریتم‌ها و روش‌های اندیس گذاری (indexing) پیاده سازی می‌شدند که تنها مشخص می‌کردند یک لغت در یک سند (document) وجود دارد یا خیر. اما با گذشت زمان محققان متوجه ناکارآمدی این دیدگاه در استخراج اطلاعات شدند. به همین دلیل روشی بنام Latent Semantic Indexing که بر پایه Latent Semantic Analysis بنا شده بود به دنیای بازیابی و استخراج اطلاعات معرف شد. کاری که این روش انجام می‌داد این بود که گامی را به مجموعه مراحل موجود در پروسه اندیس گذاری اضافه می‌کرد. این روش بجای آنکه در اندیس گذاری تنها یک متن را در نظر بگیرد و ببیند چه لغاتی در آن آورده شده است، کل مجموعه اسناد را با هم و در کنار یکدیگر در نظر می‌گرفت تا ببیند که چه اسنادی لغات مشابه با لغات موجود در سند مورد بررسی را دارند. به بیان دیگر اسناد مشابه با سند فعلی را به نوعی مشخص می‌نمود.
بر اساس دیدگاه LSI اسناد مشابه با هم، اسنادی هستند که لغات مشابه یا مشترک بیشتری داشته باشند. توجه داشته باشید تنها نمی‌گوییم لغات مشترک بیشتری بلکه از  واژه لغات مشابه نیز استفاده می‌کنیم. چرا که بر اساس LSI دو سند ممکن است هیچ لغت مشترکی نداشته باشند (یعنی لغات یکسان نداشته باشند) اما لغاتی در آنها وجود داشته باشد که به لحاظی معنایی و مفهومی هم معنا و یا مرتبط به هم باشند. بعنوان مثال لغات شش و ریه دو لغت متفاوت اما مرتبط با یکدیگر هستند و اگر دو لغات در دوسند آورده شوند می‌توان حدس زد که ارتباط و شباهتی معنایی بین آنها وجود دارد. به روش هایی که بر اساس این دیدگاه ارائه می‌شوند روش‌های جستجوی معنایی نیز گفته می‌شود. این دیدگاه مشابه دیدگاه انسانی در مواجهه با متون نیز است. انسان هنگامی که دو متن را با یکدیگر مقایسه می‌کند تنها بدنبال لغات یکسان در آن‌ها نمی‌گردد بلکه شباهت‌های معنایی بین لغات را نیز در نظر می‌گیرد این اصل و نگرش پایه و اساس الگوریتم  LSI و همچنین حوزه ای از علم بازیابی اطلاعات بنام مدل سازی موضوعی (Topic Modeling) می‌باشد.
هنگامیکه شما پرس و جویی را بر روی مجموعه ای از اسناد (که بر اساس LSI اندیس گذاری شده‌اند) اجرا می‌کنید، موتور جستجو ابتدا بدنبال لغاتی می‌گردد که بیشترین شباهت را به کلمات موجود در پرس و جوی شما دارند. بعبارتی پرس و جوی شما را بسط می‌دهد (query expansion)، یعنی علاوه بر لغات موجود در پرس و جو، لغات مشابه آنها را نیز به پرس و جوی شما می‌افزاید. پس از بسط دادن پرس و جو، موتور جستجو مطابق روال معمول در سایر روش‌های جستجو، اسنادی که این لغات (پرس و جوی بسط داده شده) در آنها وجود دارند را بعنوان نتیجه به شما باز می‌گرداند. به این ترتیب ممکن است اسنادی به شما بازگردانده شوند که لغات پرس و جوی شما در آنها وجود نداشته باشد اما LSI بدلیل وجود ارتباطات معنایی، آنها را مشابه و مرتبط با جستجو تشخیص داده باشد.  توجه داشته باشید که الگوریتم‌های جستجوی معمولی و ساده، بخشی از اسناد را که مرتبط با پرس و جو هستند، اما شامل لغات مورد نظر شما نمی‌شوند، از دست می‌دهد (یعنی کاهش recall).

برای آنکه با دیدگاه LSI بیشتر آشنا شوید در اینجا مثالی از نحوه عملکرد آن می‌زنیم. فرض کنید می‌خواهیم بر روی مجموعه ای از اسناد در حوزه زیست شناسی اندیس گذاری کنیم. بر مبنای روش LSI چنانچه لغاتی مانند کروموزم، ژن و DNA در اسناد زیادی در کنار یکدیگر آورده شوند (یا بعبارتی اسناد مشترک باهم زیادی داشته باشند)، الگوریتم جستجو چنین برداشت می‌کند که به احتمال زیاد نوعی رابطه معنایی بین آنها وجود دارد. به همین دلیل اگر شما پرس و جویی را با کلمه کلیدی "کروموزوم" اجرا نمایید، الگوریتم علاوه بر مقالاتی که مستقیما واژه کروموزوم در آنها وجود دارد، اسنادی که شامل لغات "DNA" و  "ژن" نیز باشند را بعنوان نتیجه به شما باز خواهد گرداند. در واقع می‌توان گفت الگوریتم جستجو به پرس و جوی شما این دو واژه را نیز اضافه می‌کند که همان بسط دادن پرس و جوی شما است. دقت داشته باشید که الگوریتم جستجو هیچ اطلاع و دانشی از معنای لغات مذکور ندارد و تنها بر اساس تحلیل‌های ریاضی به این نتیجه می‌رسد که در بخش‌های بعدی چگونگی آن را برای شما بازگو خواهیم نمود. یکی از برتری‌های مهم LSI نسبت به روش‌های مبتنی بر کلمات کلیدی (keyword based) این است که در LSI، ما به recall بالاتری دست پیدا می‌کنیم، بدین معنی که از کل جواب‌های موجود برای پرس و جوی شما، جواب‌های بیشتری به کاربر نمایش داده خواهند شد.
یکی از مهمترین نقاط قوت LSI اینست که این روش تنها متکی بر ریاضیات است و هیچ نیازی به دانستن معنای لغات یا پردازش کلمات در متون ندارد. این مسئله باعث می‌شود بتوان این روش را بر روی هر مجموعه متنی و با هر زبانی بکار گرفت. علاوه بر آن می‌توان LSI را بصورت ترکیبی با الگوریتم‌های جستجوی دیگر استفاده نمود و یا تنها متکی بر آن موتور جستجویی را پیاده سازی کرد.
 

نحوه عملکرد Latent Semantic Indexing
در روش LSI مبنا وقوع همزمان لغات در اسناد می‌باشد. در اصطلاح علمی به این مسئله word co-occurrence گفته می‌شود. به بیان دیگر LSI بدنبال لغاتی می‌گردد که در اسناد بیشتری در با هم آورده می‌شوند. پیش از آنکه وارد مباحث ریاضی و محاسباتی LSI شویم بهتر است کمی بیشتر در مورد این مسوله به لحاظ نظری بحث کنیم.
 
لغات زائد
به نحوه صحبت کردن روز مره انسان‌ها دقت کنید. بسیاری از واژگانی که در طول روز و در محاوره‌ها از انها استفاده می‌کنیم، تاثیری در معنای سخن ما ندارند. این مسئله در نحوه نگارش ما نیز صادق است. خیلی از لغات از جمله حروف اضافه، حروف ربط، برخی از افعال پر استفاده و غیره در جملات دیده می‌شوند اما معنای سخن ما در آنها نهفته نمی‌باشد. بعنوان مثال به جمله "جهش در ژن‌ها می‌تواند منجر به بیماری سرطان شود" درقت کنید. در این جمله لغاتی که از اهمیت بالایی بر خوردار هستند و به نوعی بار معنایی جمله بر دوش آنهاست عبارتند از "جهش"، "ژن"، بیماری" و "سرطان". بنابراین می‌توان سایر لغات مانند "در"، "می تواند" و "به" را حذف نمود. به این لغات در اصطلاح علم بازیابی اطلاعات (Information Retrieval) لغات زائد (redundant) گفته می‌شود که در اکثر الگوریتم‌های جستجو یا پردازش زبان طبیعی (natural language processing) برای رسیدن به نتایج قابل قبول باید حذف می‌شوند.روش LSI نیز از این قاعده مستثنی نیست. پیش از اجرای آن بهتر است این لغات زائد حذف گردند. این مسئله علاوه بر آنکه بر روی کیفیت نتایج خروجی تاثیر مثبت دارد، تا حد قابل ملاحظه ای کار پردازش و محاسبات را نیز تسهیل می‌نماید.
 
 
مدل کردن لغات و اسناد
پس از آنکه لغات اضافی از مجموعه متون حذف شد باید بدنبال روشی برای مدل کردن داده‌های موجود در مجموعه اسناد بگردیم تا بتوان کاربر پردازش را با توجه به آن مدل انجام داد. روشی که در LSI برای مدلسازی بکار گرفته می‌شود استفاده از ماتریس لغت – سند (term-document matrix) است. این ماتریس یک گرید بسیار بزرگ است که هر سطر از آن نماینده یک سند و هر ستون از ان نماینده یک لغت در مجموعه متنی ما می‌باشد(البته این امکان وجود دارد که جای سطر و ستون‌ها عوض شود). هر سلول از این ماتریس بزرگ نیز به نوعی نشان دهنده ارتباط بین سند و لغت متناظر با آن سلول خواهد بود. بعنوان مثال در ساده‌ترین حات می‌توان گفت که اگر لغتی در سند یافت نشد خانه متناظر با انها در ماتریس لغت – سند خالی خواهد ماند و در غیر این صورت مقدار یک را خواهد گرفت. در برخی از روش‌ها سلول‌ها را با تعداد دفعات تکرار لغات در اسناد متناظر پر می‌کنند و در برخی دیگر از معیار‌های پیچیده‌تری مانند tf*idf استفاده می‌نمایند. شکل زیر نمونه از این ماتریس‌ها را نشان می‌دهد : 

برای ایجاد چنین ماتریسی باید تک تک اسناد و لغات موجود در مجموعه متنی را پردازش نمود و خانه‌های متناظر را در ماتریس لغت – سند مقدار دهی نمود.خروجی این کار ماتریسی مانند ماتریس شکل بالا خواهد شد (البته در مقیاسی بسیار بزرگتر) که بسیاری از خانه‌های ان صفر خواهند بود (مانند آنچه در شکل نیز مشاهده می‌کنید). به این مسئله تنک بودن (sparseness) ماتریس گفته می‌شود که یکی از مشکلات استفاده از مدل ماتریس لغت – سند محسوب می‌شود. 
این ماتریس، بازتابی از کل مجموعه متنی را به ما می‌دهد. بعنوان مثال اگر بخواهیم ببینیم در سند i چه لغاتی وجود دارد، تنها کافی است به سراغ سطر iام از ماتریس برویم (البته در صورتی که ماتریس ما سند – لغت باشد) وآن را بیرون بکشیم. به این سطر در اصطلاح بردار سند (document vector) گفته می‌شود. همین کار را در مورد لغات نیز می‌توان انجام داد. بعنوان مثال با رفتن به سراغ ستون j ام می‌توان دریافت که لغت j ام  در چه اسنادی آورده شده است. به ستون j ام نیز در ماتریس سند – لغت، بردار لغت (term vector) گفته می‌شود. توجه داشته باشید که این بردار‌ها در مباحث و الگوریتم‌های مربوط به بازیابی اطلاعات و پردازش زبان طبیعی بسیار پر کاربرد می‌باشند.
با داشتن ماتریس لغت – سند می‌توان یک الگوریتم جستجو را پیاده سازی نمود. بسیاری از روش‌های جستجویی که تا کنون پیشنهاد شده اند نیز بر پایه چنین ماتریس هایی بنا شده اند. فرض کنید می‌خواهیم پرس و جویی با کلمات کلیدی "کروموزوم‌های انسان" اجرا کنیم. برای این منظور کافیست ابتدا کلمات کلیدی موجود در پرس و جو را استخراج کرده (در این مثال کروموزوم و انسان دو کلمه کلیدی ما هستند) و سپس به سراغ بردار‌های هر یک برویم. همانطور که گفته شد با مراجعه به سطر یا ستون مربوط به لغات می‌توان بردار لغت مورد نظر را یافت. پس از یافتن بردار مربوط به کروموزوم و انسان می‌توان مشخص کرد که این لغات در چه اسناد و متونی اورده شده اند و آنها را استخراج و به کاربر نشان داد. این ساده‌ترین روش جستجو بر مبنای کلمات کلیدی می‌باشد. اما دقت داشته باشید که هدف نهایی در LSI چیزی فراتر از این است. بنابراین نیاز به انجام عملیاتی دیگر بر روی این ماتریس می‌باشد که بتوانیم بر اساس آن ارتباطات معنایی بین لغات و متون را تشخیص دهیم. برای این منظور LSI ماتری لغت – سند را تجزیه (decompose) می‌کند. برای این منظور نیز از تکنیک Singular Value Decomposition استفاده می‌نماید. پیش از پرداختن به این تکنیک ابتدا بهتر است کمی با فضای برداری چند بعدی (multi-dimensional vector space) آشنا شویم. برای این منظور به مثال زیر توجه کنید.
 
مثالی از فضای چند بعدی
فرض کنید قصد دارید تحقیقی در مورد اینکه مردم چه چیز هایی را معمولا برای صبحانه خود سفارش می‌دهند انجام دهید. برای این منظور در یک روز شلوغ به رستورانی در اطراف محل زندگی خود می‌روید و لیست سفارشات صبحانه را می‌گیرید. فرض کنید از بین اقلام متعدد، تمرکز شما تنها بر روی تخم مرغ (egg)، قهوه (coffee) و بیکن (bacon) است. در واقع قصد دارید ببینید چند نفر در سفارش خود این سه قلم را باهم درخواست کرده اند. برای این منظور سفارشات را تک تک بررسی می‌کنید و تعداد دفعات را ثبت می‌کنید.
پس از آنکه کار ثبت و جمع آوری داده‌ها به پایان رسید می‌توانید نتایج را در قالب نموداری نمایش دهید. یک روش برای اینکار رسم نموداری سه بعدی است که هر بعد آن مربوط به یکی از اقلام مذکور می‌باشد. بعنوان مثال در شکل زیر نموداری سه بعدی را که برای این منظور رسم شده است مشاهده می‌کنید. همانطور که در شکل نشان داده شده است محود x مربوط به "bacon"، محور y مربوط به "egg" و محور z نیز مربوط به "coffee" می‌باشد. از آنجایی که این نمودار سه بعدی است برای مشخص کردن نقاط بر روی آن به سه عدد (x ,y ,z)  نیاز مندیم. حال اطلاعات جمع اوری شده از صورت سفارشات را یکی یکی بررسی می‌کنیم و بر اساس تعداد دفعات سفارش داده شدن این سه قلم نقطه ای  را در این فضای سه بعدی رسم می‌کنیم. بعنوان مثال اگر در سفارشی 2 عدد تخم مرغ و یک قهوه سفارش داده شد بود، این سفارش با (0, 2, 1) در نمودار ما نمایش داده خواهد شد. به این ترتیب می‌توان محل قرار گرفتن این سفارش در فضای سه بعدی سفارشات صبحانه را یافت. این کار را برای تمامی سفارشات انجام می‌دهیم تا سر انجام نموداری مانند نمودار زیر بدست آید. 

دقت داشته باشید که اگر از هریک از نقطه آغازین نمودار (0, 0, 1) خطی را به هر یک از نقاط رسم شده بکشید، بردار هایی در فضای “bacon-eggs-coffee”بدست خواهد آمد. هر کدام از این بردار‌ها به ما نشان می‌دهند که در یک صبحانه خاص بیشتر از کدام یک از این سه قلم درخواست شده است. مجموع بردار‌ها در کنار یکدیگر نیز می‌توانند اطلاعات خوبی راجع به گرایش و علاقه مردم به اقلام مذکور در صبحانه‌های خود به ما دهد. به این نمودار نمودار فضای بردار (vector – space) می‌گویند.
حالا وقت آن است که مجددا به بحث مربوط به بازیابی اطلاعات (information retrieval) باز گردیم. همانطور که گفتیم اسناد در یک مجموعه را می‌توان در قالب بردار هایی بنام Term – vector نمایش داد. این بردار‌ها مشابه بردار مثال قبل ما هستند. با این تفاوت که به جای تعداد دفعات تکرار اقلام موجود در صبحانه افراد، تعداد دفعات تکرار لغات را در یک سند در خود دارند. از نظر اندازه نیز بسیار بزرگتر از مثال ما هستند. در یک مجموعه از اسناد ما هزاران هزار لغت داریم که باید بردار‌های ما به اندازه تعداد کل لغات منحصر به فرد ما باشند. بعنوان مثال اگر در یک مجموعه ما هزار لغات غیر تکراری داریم بردار‌های ما باید هزار بعد داشته باشند. نموداری که اطلاعات را در ان نمایش خواهیم داد نیز بجای سه بعد (در مثال قبل) می‌بایست هزار بعد (یا محور) داشته باشد که البته چنین فضایی قابل نمایش نمی‌باشد.

به مثال صبحانه توجه کنید. همانطور که می‌بینید برخی از نقاط بر روی نمودار نسبت به بقیه به یکدیگر نز دیکتر هستند و ابری از نقاط را در قسمتی از نمودار ایجاد کردند. این نقاط نزدیک به هم باعث می‌شوند که بردار‌های آنها نیز با فاصله نزدیک به هم در فضای برداری مثال ما قرار گیرند. علت نزدیک بودن این بردار‌ها اینست که تعداد دفعات تکرار bacon، eggs و coffee در انها مشابه به هم بوده است. بنابراین می‌توان گفت که این نقاط (یا سفارشات مربوط به انها) به یکدیگر شبیه می‌باشند. در مورد فضای برداری مجموعه از اسناد نیز وضع به همین ترتیب است. اسنادی که لغات مشترک بیشتری با یک دیگر دارند بردار‌های مربوط به انها در فضای برداری در کنار یکدیگر قرار خواهند گرفت. هر چه این مشترکات کمتر باشد منجر به فاصله گرفتن بردار‌ها از یکدیگر می‌گردد. بنابراین می‌بینید که با داشتن فضای برداری و مقایسه بردار‌ها با یکدیگر می‌توان نتیجه گرفت که دو سند چقدر به یکدیگر شباهت دارند.
در بسیاری از روش‌های جستجو از چنین بردار هایی برای یافتن اسناد مرتبط به پرس و جوی کاربران استفاده می‌کنند. برای ان منظور تنها کافی اس پرس و جوی کاربر را بصورت برداری در فضای برداری مورد نظر نگاشت دهیم و سپس بردار حاصل را با بردار‌های مربوط به اسناد مقایسه کنیم و در نهایت آنهایی که بیشترین شباهت را دارند باز به کاربر بازگردانیم. این روش یکی از ساده‌ترین روش‌های مطرح شده در بازیابی اطلاعات است.
خوب حالا بیایید به Latent Semantic Indexing باز گردیم. روش LSI برمبنای همین فضای برداری عمل می‌کند با این تفاوت که فضای برداری را که دارای هزاران هزار بعد می‌باشد به فضای کوچکتری با ابعاد کمتر (مثلا 300 بعد) تبدیل می‌کند. به این کار در اصطلاح عملی کاهش ابعاد (dimensionality reduction) گفته می‌شود. دقت داشته باشید که هنگامیکه این عمل انجام می‌گیرد لغاتی که شباهت و یا ارتباط زیادی به لحاظ معنایی با یکدیگر دارند بجای اینکه هریک در قالب یک بعد نمایش داده شوند، همگی بصورت یک بعد در می‌آیند. بعنوان مثال لغات کروموزم و ژن از نظر معنایی با یکدیگر در ارتباط هستند. در فضای برداری اصلی این دو لغت در قالب دو بعد مجزا نمایش داده می‌شوند اما با اعمال کاهش ابعاد به ازای هر دوی آنها تنها یک بعد خواهیم داشت. مزیت این کار اینست که اسنادی که لغات مشترکی ندارند اما به لحاظ معنایی با یکدیگر ارتباط دارند در فاضی برداری کاهش یافته نزدیکی بیشتری به یکدیگر خواهند داشت.
 
روش‌های مختلفی برای اعمال کاهش ابعاد وجود دارد. در LSI از روش Singular Value Decompistion استفاده می‌شود که در بحث بعدی در مورد آن صحبت خواهیم نمود.
 
 
Singular Value Decomposition
پیشتر گفتیم که در LSI برای مدل کردن مجموعه اسناد موجود از ماتریس بزرگی بنام ماتریس لغت – سند استفاده می‌شود. این ماتریس در واقع نمایشی از مدل فضای برداری است که در بخش قبلی به آن اشاره شد. دقت داشته باشید که ما در دنیای واقعی در یک سیستم بزرگ تقریبا چیزی در حدود یک ملیون سند داریم که در مجموع این اسناد تقریبا صد هزار لغت غیر تکراری و منحصر به فرد یافت می‌شود. بنابراین می‌توان گفت میزان تنک بودن ماتریس ما تقریبا برابر با 0.1 درصد خواهد بود. یعنی از کل ماتریس تنها 0.1 درصد آن دارای اطلاعات است و اکثر سلول‌های ماتریس ما خالی می‌باشد. این مسئله را در شکل زیر می‌توانید مشاهده کنید. 

در Latent Semantic Indexing با استفاده از روش Singular Value Decomposition این ماتریس را کوچک می‌کنند. به بیان بهتر تقریبی از ماتریس اصلی را ایجاد می‌کنند که ابعاد کوچکتری خواهد داشت. این کار مزایایی را بدنبال دارد. اول آنکه سطر‌ها و ستون هایی (لغات و اسناد) که اهمیت کمی در مجموعه اسناد ما دارند را حذف می‌کند. علاوه بر آن این کار باعث می‌شود که ارتباطات معنایی بین لغات هم معنی یا مرتبط کشف شود. یافتن این ارتباطات معنایی بسیار در پاسخ به پرس و جو‌ها مفید خواهد بود. چرا که مردم معمولا در پرس و جو‌های خود از دایره لغات متفاوتی استفاده می‌کنند. بعنوان مثال برای جستجو در مورد مطالب مربوط به ژن‌های انسان برخی از واژه کروموزوم و برخی دیگر از واژه ژنوم و دیگران ممکن است از واژگان دیگری استفاده نمایند. این مسئله مشکلی را در جستجو بنام عدم تطبیق کلمات کلیدی (mismatch problem) بوجود می‌اورده که با اعمال SVD بر روی ماتریس سند – لغت این مشکل برطرف خواهد شد.
توجه داشته باشید که SVD ابعاد بردار‌های لغات و سند را کاهش می‌دهد. بعنوان مثال بجای آنکه یک سند در قالب صد هزار بعد (که هر بعد مربوط به یک لغت می‌باشد) نمایش داده شود، بصورت یک بردار مثلا 150 بعدی نمایش داده خواهد شد. طبیعی است که این کاهش ابعاد منجر به از بین رفتن برخی از اطلاعات خواهد شد چرا که ما بسیاری از ابعاد را با یکدیگر ادغام کرده ایم. این مسئله شاید در ابتدا مسئله ای نا مطلوب به نظر آید اما در اینجا نکته ای در آن نهفته است. دقت داشته باشید که آنچه از دست می‌رود اطلاعات زائد (noise) می‌باشد. از بین رفتن این اطلاعات زائد منجر می‌شود تا ارتباطات پنهان موجود در مجموعه اسناد ما نمایان گردند. با اجرای SVD بر روی ماتریس، اسناد و لغات مشابه، مشابه باقی می‌مانند و انهایی که غیر مشابه هستند نیز غیر مشابه باقی خواهد ماند. پس ما از نظر ارتباطات بین اسناد و لغات چیزی را از دست نخواهیم داد.
 
در مباحث بعدی در مورد چگونگی اعمال SVD و همچنین نحوه پاسخگویی به پرس و جو‌ها مطالب بیشتری را برای شما عزیزان خواهیم نوشت.
 
موفق و پیروز باشید. 
نظرات مطالب
ASP.NET MVC #7
برای توضیحات بیشتر در مورد Model و model به قسمت پنجم مراجعه کنید.
توضیح تکمیلی:
- کلاس پایه‌ای در ASP.NET MVC وجود دارد به نام  (^)WebViewPage. این کلاس حاوی تعاریف اولیه TempData، ViewBag، ViewData و ... Model است. این Model ریشه‌اش به اینجا بر می‌گردد و با حرف بزرگ شروع شده است. بنابراین در Viewهای سی شارپ Razor برای تعریف نوع مدل نیاز است بین model و شیء Model تفاوت وجود داشته باشد.
- در یک View شما هر تعداد مدل رو می‌تونید از طریق ViewBag و ViewData و غیره که در قسمت 5 توضیح داده شده، دریافت کنید و محدودیتی ندارد. اما این‌ها هیچکدام به معنای Strongly typed بودن View نیست. بنابراین برای حالت داشتن View از نوع Strongly typed، یکبار باید این نوع، تعریف شود.
البته یک راه هوشمندانه برای ارسال بیش از یک شیء به Model وجود دارد. یک کلاس تعریف کنید که خواص آن چندین شیء مورد نظر شما باشند. سپس این کلاس را به عنوان نوع Model در ابتدای View معرفی کنید. در اینجا به راحتی و به صورت Strongly typed با چند شیء می‌شود به عنوان Model کار کرد.
مطالب
بررسی روش مشاهده خروجی SQL حاصل از کوئری‌های Entity framework Core
هنوز تا Entity framework Core 1.1، مفهوم interceptors موجود در EF 6.x پیاده سازی نشده‌است. اما شبیه به مفاهیم «ارتقاء به ASP.NET Core 1.0 - قسمت 17 - بررسی فریم ورک Logging»، در EF Core نیز زیرساختی جهت مشاهده‌ی SQL نهایی تولیدی وجود دارد.


ایجاد یک ثبت کننده‌ی وقایع EF Core

مرحله‌ی اول مشاهده‌ی خروجی‌های نهایی EF Core، پیاده سازی اینترفیس ILoggerProvider است که در آن قرار است وهله‌ی از نوع ILogger بازگشت داده شود. به همین جهت یک کلاس تو در توی خصوصی را در اینجا مشاهده می‌کنید که اینترفیس ILogger را نیز پیاده سازی کرده‌است:
using System;
using Microsoft.Extensions.Logging;

namespace Tests
{
    public class MyLoggerProvider : ILoggerProvider
    {
        public ILogger CreateLogger(string categoryName)
        {
            return new MyLogger();
        }

        public void Dispose()
        { }

        private class MyLogger : ILogger
        {
            public bool IsEnabled(LogLevel logLevel)
            {
                return true;
            }

            public void Log<TState>(
                        LogLevel logLevel, 
                        EventId eventId, 
                        TState state, 
                        Exception exception, 
                        Func<TState, Exception, string> formatter)
            {
                //File.AppendAllText(@"C:\temp\log.txt", formatter(state, exception));
                Console.WriteLine("");
                Console.WriteLine(formatter(state, exception));
            }

            public IDisposable BeginScope<TState>(TState state)
            {
                return null;
            }
        }
    }
}
در اینجا خروجی‌هایی نهایی توسط Console.WriteLine نمایش داده شده‌اند و مناسب برنامه‌های کنسول هستند و یا می‌توان برای مثال توسط File.AppendAllText، اطلاعات رسیده را در یک فایل نیز ذخیره کرد.
در متد Log:
- پارامتر logLevel، سطح اهمیت اطلاعات رسیده را به همراه دارد. برای مثال اطلاعات است یا خطا؟
برای مثال شاید نیاز به ذخیره سازی اطلاعاتی با سطح‌های بحرانی، خطا و یا اخطار در یک بانک اطلاعاتی وجود داشته باشد:
   if (logLevel == LogLevel.Critical || logLevel == LogLevel.Error || logLevel == LogLevel.Warning)
- eventId: نوع رخداد رسیده را مشخص می‌کند.
- state: می‌تواند هر نوع شیءایی، حاوی اطلاعات وضعیت رخداد رسیده باشد.
- exception: بیانگر استثنای احتمالی رخ داده است.
- formatter: کار آن تولید یک رشته‌ی قابل خواندن، توسط اطلاعات حالت و استثناء است.


معرفی Logger تهیه شده به برنامه

پس از تهیه‌ی Logger فوق، جهت معرفی آن به یک برنامه‌ی کنسول، می‌توان به صورت ذیل عمل کرد:
using (var db = new MyContext())
{
   var loggerFactory = (ILoggerFactory)db.GetInfrastructure().GetService(typeof(ILoggerFactory));
   loggerFactory.AddProvider(new MyLoggerProvider());
 }
این ثبت تنها باید یکبار در آغاز برنامه انجام شود و پس از آن تمام وهله‌ی دیگر Context از آن استفاده خواهند کرد.

در برنامه‌های ASP.NET Core، کار معرفی MyLoggerProvider در متد Configure کلاس آغازین برنامه انجام می‌شود:
public void Configure(ILoggerFactory loggerFactory)
{
   loggerFactory.AddProvider(new MyLoggerProvider());


اختصاصی سازی ثبت وقایع رسیده

کلاس MyLoggerProvider، هر نوع اطلاعات داخلی EF Core را نیز لاگ می‌کند. اگر هدف صرفا بررسی خروجی SQL نهایی تولیدی است، می‌توان در متد ذیل:
public ILogger CreateLogger(string categoryName)
بر اساس categoryName رسیده، یا new MyLogger را بازگشت داده و یا یک NullLogger.Instance را که کاری را انجام نمی‌دهد. به این ترتیب می‌توان کار فیلتر کردن اطلاعات رسیده را انجام داد.
برای این منظور، ابتدای Logger تهیه شده چنین شکلی را پیدا می‌کند:
using Microsoft.EntityFrameworkCore.Storage.Internal;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Logging;
using System.Linq;
using System;
 
namespace Tests
{
    public class MyLoggerProvider : ILoggerProvider
    {
        private static readonly string[] _categories =
            {
                typeof(RelationalCommandBuilderFactory).FullName,
                typeof(SqlServerConnection).FullName
            };
        public ILogger CreateLogger(string categoryName)
        {
            if (_categories.Contains(categoryName))
            {
                return new MyLogger();
            }
 
            return NullLogger.Instance;
        }