Lambda Calculus via C# (7) Fixed Point Combinator and Recursion

[FP & LINQ via C# series]

[Lambda Calculus via C# series]

p is the fixed point (aka invariant point) of function f if and only if:

  p
≡ f p

Take function Math.Sqrt as example, it has 2 fix point, 0 and 1, so that 0 ≡ Math.Sqrt(0) and 1 ≡ Math.Sqrt(1).

FixedPoint.fw_thumb2_thumb

The above fixed point definition also leads to infinite substitution:

  p
≡ f p
≡ f (f p)
≡ f (f (f p))
≡ ...
≡ f (f (f ... (f p) ...))

Similarly, the fixed point combinator Y is defined as if Y f is the fixed point of f:

  (Y f)
≡ f (Y f)

Normal order fixed point combinator (Y combinator) and recursion

The following Y combinator is an implementation of fixed point combinator, discovered by Haskell Curry:

Y := λf.(λg.f (g g)) (λg.f (g g))

It is called the normal order fixed point combinator:

  Y f
≡ (λf.(λg.f (g g)) (λg.f (g g))) f
≡ (λg.f (g g)) (λg.f (g g))
≡ f ((λg.f (g g)) (λg.f (g g)))
≡ f (Y f)

y_combinator

The following is Y implemented in SKI:

Y := S (K (S I I)) (S (S (K S) K) (K (S I I)))

And just in SK:

Y := S S K (S (K (S S (S (S S K)))) K)

When Y f can also be substituted infinitely:

  (Y f)
≡ f (Y f)
≡ f (f (Y f))
≡ f (f (f (Y f)))
≡ ...
≡ f (f (f ... (f (Y f)) ...))

390px-Knights_of_the_Lambda_Calculus.svg

So Y can be used to implement recursion. As fore mentioned, in lambda calculus, a function cannot directly apply it self in its body. Take the factorial function as example, the factorial of n is defined recursively:

  • If n is greater than 0, then factorial of n is the multiplication of n and factorial of n – 1
  • if n is 0, then factorial of n is 1

So naturally:

Factorial := λn.If (n == 0) (λx.1) (λx.n * (Factorial (n - 1)))

However, in lambda calculus the above definition is illegal, because the self reference does not work anonymously:

λn.If (n == 0) (λx.1) (λx.n * (? (n - 1)))

Now with the power of Y combinator, the recursion can be implemented, but still in the anonymous way. First, in above definition, just pass the reference of itself as an variable/argument:

λf.λn.If (n == 0) (λx.1) (λx.n * (f (n - 1)))

If the above function is called FactorialHelper, then the Factorial function can be implemented as:

FactorialHelper := λf.λn.If (n == 0) (λx.1) (λx.n * (f (n - 1)))
Factorial := Y FactorialHelper

So the recursive Factorial is implemented anonymously:

  Factorial
≡ Y FactorialHelper
≡ (λf.(λg.f (g g)) (λg.f (g g))) FactorialHelper
≡ (λf.(λg.f (g g)) (λg.f (g g))) (λf.λn.If (n == 0) (λx.1) (λx.n * (f (n - 1))))

When Factorial is applied, according to the definition of Factorial and Y:

  Factorial 3
≡ Y FactorialHelper 3
≡ FactorialHelper (Y FactorialHelper) 3

Here (Y FactorialHelper) can be substituted by Factorial, according to the definition. So FactorialHelper is called with Factorial and n, exactly as expected.

The normal order Y combinator does not work with applicative order reduction. In applicative order, here FactorialHelper is applied with (Y FactorialHelper), so the right most argument Y FactorialHelper should be reduced first, which leads to infinite reduction:

  FactorialHelper (Y FactorialHelper) 3
≡ FactorialHelper (FactorialHelper (Y FactorialHelper)) 3
≡ FactorialHelper (FactorialHelper (FactorialHelper (Y FactorialHelper))) 3
≡ ...

The normal order Y combinator only works with normal order. In normal order, here FactorialHelper is applied with (Y FactorialHelper), so the left most function FactorialHelper should be reduced first:

  FactorialHelper (Y FactorialHelper) 3
≡ (λf.λn.If (n == 0) (λx.1) (λx.n * (f (n - 1)))) (Y FactorialHelper) 3
≡ (λn.If (n == 0) (λx.1) (λx.n * (Y FactorialHelper (n - 1)))) 3
≡ If (3 == 0) (λx.1) (λx.3 * (Y FactorialHelper (3 - 1)))
≡ If (False) (λx.1) (λx.3 * (Y FactorialHelper (3 - 1))
≡ 3 * (Y FactorialHelper (3 - 1))
≡ 3 * (FactorialHelper (Y FactorialHelper) (3 - 1))
≡ 3 * ((λf.λn.If (n == 0) (λx.1) (λx.n * (f (n - 1)))) (Y FactorialHelper) (3 - 1))
≡ 3 * ((λn.If (n == 0) (λx.1) (λx.n * (Y FactorialHelper (n - 1)))) (3 - 1))
≡ 3 * (If ((3 - 1) == 0) (λx.1) (λx.(3 - 1) * (Y FactorialHelper ((3 - 1) - 1))))
≡ 3 * ((3 - 1) * (Y FactorialHelper ((3 - 1) - 1)))
≡ 3 * (2 * (Y FactorialHelper ((3 - 1) - 1)))
≡ 3 * (2 * (FactorialHelper (Y FactorialHelper) ((3 - 1) - 1)))
≡ 3 * (2 * ((λf.λn.If (n == 0) (λx.1) (λx.n * (f (n - 1)))) (Y FactorialHelper) ((3 - 1) - 1)))
≡ 3 * (2 * ((λn.If (n == 0) (λx.1) (λx.n * (Y FactorialHelper (n - 1)))) ((3 - 1) - 1)))
≡ 3 * (2 * (If (((3 - 1) - 1) == 0) (λx.1) (λx.((3 - 1) - 1) * (Y FactorialHelper (((3 - 1) - 1) - 1)))))
≡ 3 * (2 * (((3 - 1) - 1) * (Y FactorialHelper (((3 - 1) - 1) - 1))))
≡ 3 * (2 * (1 * (Y FactorialHelper (((3 - 1) - 1) - 1))))
≡ 3 * (2 * (1 * (FactorialHelper (Y FactorialHelper) (((3 - 1) - 1) - 1))))
≡ 3 * (2 * (1 * ((f.λn.If (n == 0) (λx.1) (λx.n * (f (n - 1)))) (Y FactorialHelper) (((3 - 1) - 1) - 1))))
≡ 3 * (2 * (1 * ((n.If (n == 0) (λx.1) (λx.n * (Y FactorialHelper (n - 1)))) (((3 - 1) - 1) - 1))))
≡ 3 * (2 * (1 * (If ((((3 - 1) - 1) - 1) == 0) (λx.1) (λx.(((3 - 1) - 1) - 1) * (Y FactorialHelper ((((3 - 1) - 1) - 1) - 1))))))
≡ 3 * (2 * (1 * 1))

So the Y f infinite reduction is blocked in in normal order reduction. First, Y f is reduced to f (Y f), then the next reduction is to reduce leftmost expression f, not the the rightmost (Y f). In the above example Y FactorialHelper n:

  • If n is greater than 0, Y Factorial n is reduced to n * (Y Factorial (n - 1)), where Y Factorial can be further reduced, so the recursion continues.
  • If n is 0, Y Factorial n is reduced to 1. The reduction ends, so the recursion terminates.

Y combinator is easy to implement in C#. Generally, for a recursive function f of type T -> TResult, its helper function accepts the T -> TResult function and a T value, then return TResult, so its helper function is of type (T -> TResult) –> T -> TResult. Y can be viewed as accepting helper function and returns f. so Y is of type ((T -> TResult) –> T -> TResult) -> (T -> TResult). So:

public static partial class FixedPointCombinators<T, TResult>
{
    // Y = (g => f(g(g)))(g => f(g(g)))
    public static readonly Func<Func<Func<T, TResult>, Func<T, TResult>>, Func<T, TResult>>
        Y = f => new SelfApplicableFunc<Func<T, TResult>>(g => f(g(g)))(g => f(g(g)));
}

Here are the types of the elements in above lambda expression:

  • g: SelfApplicableFunc<T -> TResult>
  • g(g): T -> TResult
  • f: (T -> TResult) –> T -> TResult
  • f(g(g)): T => TResult
  • g => f(g(g)): SelfApplicableFunc<T -> TResult> –> T -> TResult, which is SelfApplicableFunc<T -> TResult> by definition
  • (g => f(g(g)))(g => f(g(g))): T -> TResult

For Factorial, apparently it is of function type Numeral -> Numeral, so FactorialHelper is of function type (Numeral -> Numeral) –> Numeral -> Numeral:

using static FixedPointCombinators<Numeral, Numeral>;

public static partial class ChurchNumeral
{
    // FactorialHelper = factorial => n => If(n == 0)(_ => 1)(_ => n * factorial(n - 1))
    public static readonly Func<Func<Numeral, Numeral>, Func<Numeral, Numeral>>
        FactorialHelper = factorial => n =>
            If(n.IsZero())
                (_ => One)
                (_ => n.Multiply(factorial(n.Subtract(One))));

    public static readonly Func<Numeral, Numeral>
        Factorial = Y(FactorialHelper);
}

Calling above Factorial always throws StackOverflowException, because in C# executes in applicative order. When Factorial is called, it calls normal order Y in applicative order, which causes infinite execution.

Applicative order fixed point combinator (Z combinator) and recursion

The above Y combinator does not work in C#. When reducing Y f in applicative order, the self application in expression f (g g) leads to infinite reduction, which need to be blocked. The solution is to eta convert f (g g) to λx.f (g g) x. So the applicative order fixed point combinator is:

Z := λf.(λg.λx.f (g g) x) (λg.λx.f (g g) x)

It is called Z combinator. Now reduce Z f in applicative order:

  Z f
≡ (λf.(λg.λx.f (g g) x) (λg.λx.f (g g) x)) f
≡ (λg.λx.f (g g) x) (λg.λx.f (g g) x)
≡ λx.f ((λg.λx.f (g g) x) (λg.λx.f (g g) x)) x
≡ λx.f (Z f) x

This time Z f is not reduced to f (Z f), but reduced to the eta expanded version λx.f (Z f) x, so any further reduction is blocked. Still take factorial as example:

  Factorial 3
≡ Z FactorialHelper 3
≡ (λx.FactorialHelper (Z FactorialHelper) x) 3
≡ FactorialHelper (Z FactorialHelper) 3
≡ FactorialHelper (λx.FactorialHelper (Z FactorialHelper) x) 3
≡ (λf.λn.If (n == 0) (λx.1) (λx.n * (f (n - 1)))) (λx.FactorialHelper (Z FactorialHelper) x) 3
≡ (λn.If (n == 0) (λx.1) (λx.n * ((λx.FactorialHelper (Z FactorialHelper) x) (n - 1)))) 3
≡ If (3 == 0) (λx.1) (λx.3 * ((λx.FactorialHelper (Z FactorialHelper) x) (3 - 1)))
≡ If (False) (λx.1) (λx.3 * ((λx.FactorialHelper (Z FactorialHelper) x) (3 - 1)))
≡ 3 * ((λx.FactorialHelper (Z FactorialHelper) x) (3 - 1))
≡ 3 * ((λx.FactorialHelper (Z FactorialHelper) x) 2)
≡ 3 * (FactorialHelper (Z FactorialHelper) 2)
≡ 3 * (FactorialHelper (λx.FactorialHelper (Z FactorialHelper) x) 2)
≡ 3 * ((λf.λn.If (n == 0) (λx.1) (λx.n * (f (n - 1)))) (λx.FactorialHelper (Z FactorialHelper) x) 2)
≡ 3 * ((λn.If (n == 0) (λx.1) (λx.n * ((λx.FactorialHelper (Z FactorialHelper) x) (n - 1)))) 2)
≡ 3 * (If (2 == 0) (λx.1) (λx.2 * ((λx.FactorialHelper (Z FactorialHelper) x) (2 - 1))))
≡ 3 * (If (False) (λx.1) (λx.2 * ((λx.FactorialHelper (Z FactorialHelper) x) (2 - 1))))
≡ 3 * (2 * ((λx.FactorialHelper (Z FactorialHelper) x) (2 - 1)))
≡ 3 * (2 * ((λx.FactorialHelper (Z FactorialHelper) x) 1))
≡ 3 * (2 * (FactorialHelper (Z FactorialHelper) 1))
≡ 3 * (2 * (FactorialHelper (λx.FactorialHelper (Z FactorialHelper) x) 1))
≡ 3 * (2 * ((λf.λn.If (n == 0) (λx.1) (λx.n * (f (n - 1)))) (λx.FactorialHelper (Z FactorialHelper) x) 1))
≡ 3 * (2 * ((λn.If (n == 0) (λx.1) (λx.n * ((λx.FactorialHelper (Z FactorialHelper) x) (n - 1)))) 1))
≡ 3 * (2 * (If (1 == 0) (λx.1) (λx.1 * ((λx.FactorialHelper (Z FactorialHelper) x) (1 - 1)))))
≡ 3 * (2 * (If (False) (λx.1) (λx.1 * ((λx.FactorialHelper (Z FactorialHelper) x) (1 - 1)))))
≡ 3 * (2 * (1 * ((λx.FactorialHelper (Z FactorialHelper) x) (1 - 1))))
≡ 3 * (2 * (1 * ((λx.FactorialHelper (Z FactorialHelper) x) 0)))
≡ 3 * (2 * (1 * (FactorialHelper (Z FactorialHelper) 0)))
≡ 3 * (2 * (1 * (FactorialHelper (λx.FactorialHelper (Z FactorialHelper) x) 0)))
≡ 3 * (2 * (1 * ((λf.λn.If (n == 0) (λx.1) (λx.n * (f (n - 1)))) (λx.FactorialHelper (Z FactorialHelper) x) 0)))
≡ 3 * (2 * (1 * ((λn.If (n == 0) (λx.1) (λx.n * ((λx.FactorialHelper (Z FactorialHelper) x) (n - 1)))) 0)))
≡ 3 * (2 * (1 * (If (0 == 0) (λx.1) (λx.0 * ((λx.FactorialHelper (Z FactorialHelper) x) (n - 1))))))
≡ 3 * (2 * (1 * (If (True) (λx.1) (λx.0 * ((λx.FactorialHelper (Z FactorialHelper) x) (n - 1))))))
≡ 3 * (2 * (1 * 1))

In C#, Z combinator can be implemented in the same pattern. Just eta expand f(g(g)) to x => f(g(g))(x):

public static partial class FixedPointCombinators<T, TResult>
{
    // Z = (g => x => f(g(g))(x))(g => x => f(g(g))(x))
    public static readonly Func<Func<Func<T, TResult>, Func<T, TResult>>, Func<T, TResult>>
        Z = f => new SelfApplicableFunc<Func<T, TResult>>(g => x => f(g(g))(x))(g => x => f(g(g))(x));
}

The types of the elements in above lambda expression are the same as in Y combinator, and x is of type T.

Now Factorial can be defined with Z and above FactorialHelper:

using static ChurchBoolean;
using static FixedPointCombinators<Numeral, System.Func<Numeral, Numeral>>;

public static partial class ChurchNumeral
{
    // DivideByHelper = divideBy => dividend => divisor => If(dividend >= divisor)(_ => 1 + divideBy(dividend - divisor)(divisor))(_ => 0)
    private static readonly Func<Func<Numeral, Func<Numeral, Numeral>>, Func<Numeral, Func<Numeral, Numeral>>> DivideByHelper = divideBy => dividend => divisor =>
            If(dividend.IsGreaterThanOrEqualTo(divisor))
                (_ => One.Add(divideBy(dividend.Subtract(divisor))(divisor)))
                (_ => Zero);

    public static readonly Func<Numeral, Func<Numeral, Numeral>> 
        DivideBy = Z(DivideByHelper);
}

Another recursion example is Fibonacci number. The nth Fibonacci number is defined recursively:

  • if n is greater than 1, then the nth Fibonacci number is the sum of the (n -1)th Fibonacci number and the (n -2)th Fibonacci number.
  • if n is 1 or 0, then the nth Fibonacci number is n

So naturally:

Fibonacci := λn.If (n > 1) (λx.(Fibonacci (n - 1)) + (Fibonacci (n - 2))) (λx.n)

Again, the above recursive definition is illegal in lambda calculus, because the self reference does not work anonymously:

λn.If (n > 1) (λx.(? (n - 1)) + (? (n - 2))) (λx.n)

Following the same helper function pattern as FactorialHelper, a FibonacciHelper can be defined to pass the Fibonacci function as a variable/argument, then Fibonacci can be defined with Z and FibonacciHelper:

FibonacciHelper := λf.λn.If (n > 1) (λx.(f (n - 1)) + (f (n - 2))) (λx.n)
Fibonacci := Z FibonacciHelper

Now Fibonacci is recursive but still can go anonymous, without any self reference:

  Fibonacci
≡ Z FibonacciHelper
≡ (λf.(λg.λx.f (g g) x) (λg.λx.f (g g) x)) FibonacciHelper
≡ (λf.(λg.λx.f (g g) x) (λg.λx.f (g g) x)) (λf.λn.If (n > 1) (λx.(f (n - 1)) + (f (n - 2))) (λx.n))

In C#:

// FibonacciHelper  = fibonacci  => n => If(n > 1)(_ => fibonacci(n - 1) + fibonacci(n - 2))(_ => n)
private static readonly Func<Func<Numeral, Numeral>, Func<Numeral, Numeral>>
    FibonacciHelper = fibonacci => n =>
        If(n.IsGreaterThan(One))
            (_ => fibonacci(n.Subtract(One)).Add(fibonacci(n.Subtract(Two))))
            (_ => n);

// Fibonacci = Z(FibonacciHelper)
public static readonly Func<Numeral, Numeral>
    Fibonacci = Z(FibonacciHelper);

Previously, in the Church numeral arithmetic, the following illegal DivideBy with self reference was temporarily used:

DivideBy := λa.λb.If (a >= b) (λx.1 + (DivideBy (a - b) b)) (λx.0)

Finally, with Z, an legal DivideBy in lambda calculus can be defined, following the same helper function pattern:

DivideByHelper := λf.λa.λb.If (a >= b) (λx.1 + (f (a - b) b)) (λx.0)
DivideBy := Z DivideByHelper

The following is the formal version of DivideBy:

  DivideBy
≡ Z DivideByHelper
≡ (λf.(λg.λx.f (g g) x) (λg.λx.f (g g) x)) DivideByHelper
≡ (λf.(λg.λx.f (g g) x) (λg.λx.f (g g) x)) (λf.λa.λb.If (a >= b) (λx.1 + (f (a - b) b)) (λx.0))

In C#:

// DivideByHelper = divideBy => dividend => divisor => If(dividend >= divisor)(_ => 1 + divideBy(dividend - divisor)(divisor))(_ => 0)
private static readonly Func<Func<Numeral, Func<Numeral, Numeral>>, Func<Numeral, Func<Numeral, Numeral>>>
    DivideByHelper = divideBy => dividend => divisor =>
        If(dividend.IsGreaterThanOrEqualTo(divisor))
            (_ => One.Add(divideBy(dividend.Subtract(divisor))(divisor)))
            (_ => Zero);

// DivideBy = Z(DivideByHelper)
public static readonly Func<Numeral, Func<Numeral, Numeral>>
    DivideBy = Z(DivideByHelper);

The following are a few examples

public static partial class NumeralExtensions
{
    public static Numeral Factorial(this Numeral n) => ChurchNumeral.Factorial(n);

    public static Numeral Fibonacci(this Numeral n) => ChurchNumeral.Fibonacci(n);

    public static Numeral DivideBy(this Numeral dividend, Numeral divisor) => 
        ChurchNumeral.DivideBy(dividend)(divisor);
}

[TestClass]
public partial class FixedPointCombinatorTests
{
    [TestMethod]
    public void FactorialTest()
    {
        Func<uint, uint> factorial = null; // Must have to be compiled.
        factorial = x => x == 0 ? 1U : x * factorial(x - 1U);

        Assert.AreEqual(factorial(0U), 0U.Church().Factorial().Unchurch());
        Assert.AreEqual(factorial(1U), 1U.Church().Factorial().Unchurch());
        Assert.AreEqual(factorial(2U), 2U.Church().Factorial().Unchurch());
        Assert.AreEqual(factorial(8U), 8U.Church().Factorial().Unchurch());
    }

    [TestMethod]
    public void FibonacciTest()
    {
        Func<uint, uint> fibonacci = null; // Must have. So that fibonacci can recursively refer itself.
        fibonacci = x => x > 1U ? fibonacci(x - 1) + fibonacci(x - 2) : x;

        Assert.AreEqual(fibonacci(0U), 0U.Church().Fibonacci().Unchurch());
        Assert.AreEqual(fibonacci(1U), 1U.Church().Fibonacci().Unchurch());
        Assert.AreEqual(fibonacci(2U), 2U.Church().Fibonacci().Unchurch());
        Assert.AreEqual(fibonacci(8U), 8U.Church().Fibonacci().Unchurch());
    }

    [TestMethod]
    public void DivideByTest()
    {
        Assert.AreEqual(1U / 1U, 1U.Church().DivideBy(1U.Church()).Unchurch());
        Assert.AreEqual(1U / 2U, 1U.Church().DivideBy(2U.Church()).Unchurch());
        Assert.AreEqual(2U / 2U, 2U.Church().DivideBy(2U.Church()).Unchurch());
        Assert.AreEqual(2U / 1U, 2U.Church().DivideBy(1U.Church()).Unchurch());
        Assert.AreEqual(8U / 3U, 8U.Church().DivideBy(3U.Church()).Unchurch());
        Assert.AreEqual(3U / 8U, 3U.Church().DivideBy(8U.Church()).Unchurch());
    }
}

53 Comments

  • Nicely Explained...thanks for sharing..

  • So much effort! Thanks for making it clear !

  • Is it possible to round a square from the second digit after the dot? I tried to do a calculation that tells people how much the service will cost. Using a calculator on site http://seo-google.co.il/. I could not get to the circle of numbers. Maybe there are more ideas?

  • Very much informative blog, thanks for sharing.

  • Thanks for informative post

  • The education was excellent

  • بسیار زیبا است

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

  • This article is really fantastic and thanks for sharing the valuable post.

  • Great article with excellent idea! I have bookmarked your site since this site contains important data in it.

  • awesome post. I’m a normal visitor of your web site and appreciate you taking the time to maintain the nice site. I’ll be a frequent visitor for a long time.

  • This post is really astounding one! I was delighted to read this, very much useful. Many thanks

  • Thanks for sharing.I found a lot of interesting information here. A really good post, very thankful and hopeful that you will write many more

  • Great Article it its really informative and innovative keep us posted with new updates. its was really valuable. 

  • Thanks for writing such a good article, I stumbled onto your blog and read a few post. I like your style of writing...

  • Great Post !! Very interesting topic will bookmark your site to check if you write more about in the future.

  • Very interesting topic will bookmark your site to check if you Post more about in the future.

  • شرکت تهویه نوین ایرانیان با بهره گیری از کادری مجرب و حرفه ای، متشکل از مهندسین با تجربه و نیروهای متخصص بر آن است تا در مسیر تحقق مشتری مداری گامهایی مؤثرتر بردارد. در این راستا با ارائه محصولاتی با کیفیت، عملکردی مطلوب، هزینه ای بهینه و نیز خدمات پس از فروش، در پی جلب رضایت مشتریان گرامی است.

  • https://ma-study.blogspot.com/

  • 60 days game time is currently the only game time provided by blizzard for gamers, Word of Warcraft. In the past, there were games like 30-day and 180-day, but due to the new policies of this company and the policy that it has considered, the only game time that can be provided for dear gamers is Game Time 60. Is fasting. In the following, we have collected interesting explanations about game time for you, which are worth reading.

    Two months gametime application

    Currently, 2 months gametime is used in all areas of world of warcraft. But if you want to experience a series of exciting and new experiences, you have to buy this game time. These experiences include:
    Use new expansions
    Play in new maps
    Roll up in a new style
    Change in the shape of the game
    Preparing all kinds of game time from Jet Game site
    We are waiting for your support

  • خرید گیم تایم 60 روزه

     خرید گیم تایم 60 روزه: بدون شک همه ی دوستداران بازی های آنلاین چندین سال است که با نام بازی  ورلد آف وارکرافت آشنا هستند ، بازی وارکرافت یکی از بازی های پر طرفدار و جذاب در بین گیم های آنلاین چند نفره است که توسط شرکت بلیزارد ارائه شد.

  • خرید گیم تایم 60 روزه

     خرید گیم تایم 60 روزه: بدون شک همه ی دوستداران بازی های آنلاین چندین سال است که با نام بازی  ورلد آف وارکرافت آشنا هستند ، بازی وارکرافت یکی از بازی های پر طرفدار و جذاب در بین گیم های آنلاین چند نفره است که توسط شرکت بلیزارد ارائه شد.

  • گیم تایم 60 روزه در حال حاضر تنها گیم تایمی است که از طرف کمپانی blizzard برای بازیکنان گیم ، ورد اف وارکرافت ارائه شده است. در گذشته گیم تایم هایی مانند 30 روزه و 180 روزه هم موجود بود اما به دلیل سیاست های جدید این کمپانی و خط مشی که در نظر گرفته است، تنها گیم تایمی که در حال حاضر امکان فراهم کردن آن برای گیمر های عزیز، گیم تایم 60 روزه می باشد. در ادامه توضیحات جالبی در مورد گیم تایم برای شما جمع آوری کرده ایم که خواندنشان خالی از لطف نیست.

    کاربرد گیم تایم دو ماهه

    در حال حاضر گیم تایم 2 ماهه در تمامی زمینه های world of warcraft کاربرد دارد. اما اگر می خواهید که یک سری تجربه های جذاب و جدید را تجربه کنید باید این گیم تایم را خریداری کنید. این تجربه ها عبارتند از:
    استفاده از اکسپنشن های جدید
    بازی در مپ های جدید
    لول آپ به سبک جدید
    تغییر در شکل بازی

  • گیم تایم 60 روزه در حال حاضر تنها گیم تایمی است که از طرف کمپانی blizzard برای بازیکنان گیم ، ورد اف وارکرافت ارائه شده است. در گذشته گیم تایم هایی مانند 30 روزه و 180 روزه هم موجود بود اما به دلیل سیاست های جدید این کمپانی و خط مشی که در نظر گرفته است، تنها گیم تایمی که در حال حاضر امکان فراهم کردن آن برای گیمر های عزیز، گیم تایم 60 روزه می باشد. در ادامه توضیحات جالبی در مورد گیم تایم برای شما جمع آوری کرده ایم که خواندنشان خالی از لطف نیست.

    کاربرد گیم تایم دو ماهه

    در حال حاضر گیم تایم 2 ماهه در تمامی زمینه های world of warcraft کاربرد دارد. اما اگر می خواهید که یک سری تجربه های جذاب و جدید را تجربه کنید باید این گیم تایم را خریداری کنید. این تجربه ها عبارتند از:
    استفاده از اکسپنشن های جدید
    بازی در مپ های جدید
    لول آپ به سبک جدید
    تغییر در شکل بازی

  • Looking at this article, I miss the time when I didn't wear a mask. Keo nha cai Hopefully this corona will end soon. My blog is a blog that mainly posts pictures of daily life before Corona and landscapes at that time. If you want to remember that time again, please visit us.

  • تابلو برق سه فاز چیست؟

    تابلو برق سه فاز یک محفظه فلزی یا غیر فلزی برای تعبیه و نصب تجهیزات الکترونیکی و برقی مختلف می باشد و کلیه کلیدها، سوئیچ های حفاظتی و کنترلی و نمایشگرهای فرکانس، جریان، ولتاژ، توان و غیره بر اساس نقشه روی آن نصب می شوند. هم چنین، تابلو برق 3 فاز مانع می شود که به اپراتور تجهیزات شوک الکتریکی وارد شود و از تجهیزات داخلی در برابر عوامل محیطی هم محافظت می کند.

    تابلو برق های سه فاز بر طبق استاندارد ساخته می شوند و ابعاد آن ها مختلف است و گاهی در اندازه یک محفظه کوچک هستند که چند کلید مینیاتوری در آن ها قرار می گیرد و گاهی به بزرگی یک یا چند اتاق بزرگ می باشند

  • It's too bad to check your article late. I wonder what it would be if we met a little faster. I want to exchange a little more, but please visit my site bitcoincasino and leave a message!!

  • تاسیسات مکانیکی ساختمان یکی از عوامل مهم و حیاتی برای ساختمان است که به مرور زمان دچار پیشرفت و تغییر شده است و باید به صورت اصولی و دقیق انجام شود زیرا در صورتی که به طور صحیح طراحی نشود، ساکنین ساختمان را دچار مشکل می کند؛ بنابراین می توان گفت تاسیسات مکانیکی ساختمان مانند سیستم شریان بدن انسان است که برای بهبود سطح آسایش زندگی بشر مورد استفاده قرار می گیرد و طراحی مناسب باید توسط متخصصین و مهندسین با تجربه انجام شود. در طراحی این تاسیسات چگونگی قرار گیری ساختمان، آب و هوا، دانش مهندسین، نظر کارفرما و … بسیار مهم است.

  • The most complete database in the field of electrical and mechanical building design training

  • I agree with your article. And I want to know you more. You weighed in on the day when I really wanted to receive new news.

  • Your writing is perfect and complete. totosite However, I think it will be more wonderful if your post includes additional topics that I am thinking of. I have a lot of posts on my site similar to your topic. Would you like to visit once?

  • I like your all post. You have done really good work. .Thank you for the information.

  • Everything looks different but interesting in itself. Are you really the one who wrote it? Very good. I've read your article, it's really good, you have a really creative idea. It's all interesting.

  • Enter the payment details, such as your card number and the other stuff asked by it. After this, confirm the payment and verify for the same.

  • Someone is getting through something hard right now because you've got their back. Nice work.

  • Tak mungkin kamu menemukan situs terbaik selain di <a href="https://bursa188.pro/"rel="dofollow">BURSA188</a> <a href="https://bursa188.store/"rel="dofollow">BURSA188</a>

  • I think that's one of the very important information for me. And I'm glad to study your article. But I'd like to comment on some general things. The website tastes good, and the articles are actually great: D. Good process, cheers.

  • در ساخت این فیبر از نوعی کاغذ عایقی مخصوص و رزین فنولیک به عنوان مواد پایه استفاده شده است، فیبر استخوانی تحت فشار و دمای بالا تولید شده و به همین دلیل از تراکم فوق العاده و استحکام مکانیکی و مقاومت الکتریکی بسیار عالی برخوردار بوده و جذب رطوبت پایین و مقاومت در برابر مواد شیمیایی از دیگر مزایای فیبرهای استخوانی است.

  • Everything is really fitting. There are both content that can not be read anywhere.

  • I was looking for another article by chance and found your article <a href="https://images.google.sn/url?sa=t&url=https%3A%2F%2Fwww.mtclean.blog/">baccaratsite</a> I am writing on this topic, so I think it will help a lot. I leave my blog address below. Please visit once.

  • I came to this site with the introduction of a friend around me and I was very impressed when I found your writing. I'll come back often after bookmarking! <a href="https://images.google.sm/url?sa=t&url=https%3A%2F%2Fwww.mtclean.blog/">slotsite</a>

  • It's the same topic , but I was quite surprised to see the opinions I didn't think of. My blog also has articles on these topics, so I look forward to your visit. <a href="https://images.google.sk/url?sa=t&url=https%3A%2F%2Fwww.mtclean.blog/">baccaratcommunity</a>

  • Why couldn't I have the same or similar opinions as you? T^T I hope you also visit my blog and give us a good opinion. <a href="https://images.google.si/url?sa=t&url=https%3A%2F%2Fwww.mtclean.blog/">casinocommunity</a>

  • 123

  • Board Model Papers 2024 provide all states of 6th to 10th text books 2024 Candidates who are Searching for 6th to 10th and 11th to 12th text books and syllabus, sample questions, exam pattern, and Co-Curricular Subject textbooks can refer to this entire article. https://boardmodelpaper.com/ and question papers for following the website and Arts, Science, Commerce Stream Subject Wise Solved Question Bank for Hindi & English Medium Students with Exam Pattern & Blueprint and subject Wise with 11th & 12th Question Bank 2024 for General & Vocational Course. Here, we have gathered all subjects of Board textbooks for all Class along with the direct download links.

  • I am genuinely thankful to the holder of this web page who has shared this wonderful paragraph at at this place.

  • The post is written in very a good manner and it contains many useful information for me.

  • Attractive, post. I just stumbled upon your weblog and wanted to say that I have liked browsing your blog posts. After all, I will surely subscribe to your feed, and I hope you will write again soon!

  • I recently came across your blog and have been reading along. I thought I would leave my first comment

  • Excellent article. The writing style which you have used in this article is very good and it made the article of better quality.

  • I really found this to much informatics. It is what i was searching for.I would like to suggest you that please keep sharing such type of info.Thanks

  • Pretty good post. I just stumbled upon your blog and wanted to say that I have really enjoyed reading your blog posts.

  • Thanks for sharing this informative blog, I appreciate the effort you put into providing such detailed information

Add a Comment

As it will appear on the website

Not displayed

Your website