Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions F#/LambdaInterpreter.Test/LambdaInterpreter.Test.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>

<IsPackable>false</IsPackable>
<GenerateProgramFile>true</GenerateProgramFile>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<Compile Include="LambdaInterpreterTest.fs" />

</ItemGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="FsUnit" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="NUnit" Version="4.1.0" />
<PackageReference Include="NUnit.Analyzers" Version="3.9.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\LambdaInterpreter\LambdaInterpreter.fsproj" />
</ItemGroup>

</Project>
46 changes: 46 additions & 0 deletions F#/LambdaInterpreter.Test/LambdaInterpreterTest.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
module LambdaInterpreter.Test

open NUnit.Framework
open FsUnit

let S = Abstr("x", Abstr("y", Abstr("z", App(App(Var("x"), Var("z")), App(Var("y"), Var("z"))))))
let K = Abstr("x", Abstr("y", Var("x")))
let KStar = Abstr("y", Abstr("x", Var("x")))
let Ix = Abstr("x", Var("x"))
let Iz = Abstr("z", Var("z"))

[<Test>]
let getAllNamesTest () =
App(Abstr("a", Abstr("x", App(Var("a"), Var("x")))), Var("b")) |> getAllNames |> should equal (Set.ofList [ "a"; "x"; "b"])

[<Test>]
let getNameTest () =
App(Abstr("a", Abstr("b", App(App(Var("a"), Var("b1")), Var("b")))), Var("b")) |> generateName "b" |> should equal "b2"

[<Test>]
let alphaTest () =
Abstr("b", App(Var("a"), Var("b"))) |> alphaTransform |> should equal (Abstr("b1", App(Var("a"), Var("b1"))))

[<Test>]
let betaTest () =
App(Abstr("a", Abstr("b", App(App(Var("a"), Var("b1")), Var("b")))), Var("b")) |> betaTransform |> should equal (Abstr("b2", App(App(Var("b"), Var("b1")), Var("b2"))))

[<Test>]
let varNormalizeTest () =
Var("a") |> normalize |> should equal (Var("a"))

[<Test>]
let INormalizeTest () =
Ix |> normalize |> should equal Ix

[<Test>]
let renameNormalizeTest () =
App(Abstr("a", Abstr("b", App(Var("a"),Var("b")))), Var("b")) |> normalize |> should equal (Abstr("b1", App(Var("b"),Var("b1"))))

[<Test>]
let KIKStarTest () =
App(K, Ix) |> normalize |> should equal KStar

[<Test>]
let SkkTest () =
App(App(S, K), K) |> normalize |> should equal Iz
120 changes: 120 additions & 0 deletions F#/LambdaInterpreter/LambdaInterpreter.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
module LambdaInterpreter

/// <summary>
/// Represents lamda term
/// </summary>
type LambdaTerm =
| Var of string
| App of LambdaTerm * LambdaTerm
| Abstr of string * LambdaTerm

/// <summary>
/// Recursively maps given function to the lamda term, skipping recursive mapping, when needed
/// </summary>
/// <param name="mapping"> The function to produce a new term from the given one, and the condition of whether recursion inside current term should be skipped </param>
/// <param name="term"> Term to map </param>
/// <returns> Mapped term </returns>
let rec mapTerm mapping term =
let (mappedTerm, skip) = mapping term
if not skip then
match mappedTerm with
| Var _ -> mappedTerm
| App(lhs, rhs) -> App(mapTerm mapping lhs, mapTerm mapping rhs)
| Abstr (param, rhs) -> Abstr(param, mapTerm mapping rhs)
else
mappedTerm

/// <summary>
/// Applies function to each term recursively threading an accumulator argument through computation
/// </summary>
/// <param name="mapping"> Function to update accumulator based on term value </param>
/// <param name="acc"> The initial state </param>
/// <param name="term"> Term to fold </param>
/// <returns> Mapped term </returns>
let bfsFoldTerm fold acc term =
let rec bfsFoldHelper fold acc queue =
match queue with
| [] -> acc
| Var(var)::tail ->
bfsFoldHelper fold (fold (Var(var)) acc) tail
| App(lhs, rhs)::tail ->
bfsFoldHelper fold (fold (App(lhs, rhs)) acc) (tail @ [ lhs; rhs ])
| Abstr(param, rhs)::tail ->
bfsFoldHelper fold (fold (Abstr(param, rhs)) acc) (tail @ [ rhs ])
bfsFoldHelper fold acc [ term ]

/// <summary>
/// Returns set of all variable names in term
/// </summary>
/// <param name="mainTerm"> Term to search names from </param>
/// <returns> Set of names </returns>
let getAllNames mainTerm =
let fold term acc =
match term with
| Var v -> Set.add v acc
| Abstr(param, _) -> Set.add param acc
| _ -> acc
bfsFoldTerm fold Set.empty mainTerm

/// <summary>
/// Generates new variable name based on give one, which doesnt interrupt with given term
/// </summary>
/// <param name="oldName"> Name to base new one from </param>
/// <param name="mainTerm"> Term to search names from </param>
/// <returns> New name </returns>
let generateName oldName mainTerm =
let freeNames = getAllNames mainTerm
let rec generateNameHelper counter =
let newName = oldName + counter.ToString()
if freeNames.Contains(newName) then generateNameHelper (counter + 1) else newName
generateNameHelper 1

/// <summary>
/// Applies alpha transformation on given term
/// </summary>
/// <param name="term"> Term to apply transofrmation to </param>
/// <returns> Transformed term </returns>
let alphaTransform term =
match term with
| Abstr(param, subterm) ->
let newName = generateName param subterm
let mapping mappingTerm =
match mappingTerm with
| Var v when v.Equals(param)-> (Var(newName), false)
| _ -> (mappingTerm, false)
Abstr(newName, mapTerm mapping subterm)
| _ -> term

/// <summary>
/// Applies beta transformation based on normal strategy on given term
/// </summary>
/// <param name="mainTerm"> Term to apply transofrmation to </param>
/// <returns> Transformed term </returns>
let betaTransform mainTerm =
let reduceMatch term acc =
match (term, acc) with
| (App(Abstr(_,_), _), None) -> Some(term)
| _ -> acc
let reduceTerm = bfsFoldTerm reduceMatch None mainTerm
match reduceTerm with
| None -> mainTerm
| Some(App(Abstr(param, abstrTerm), appTerm)) ->
let allNames = getAllNames appTerm
let reduceMapping term =
match term with
| Var v when v.Equals(param) -> (appTerm, true)
| Abstr(p, _) when allNames.Contains(p) -> (alphaTransform term, false)
| _ -> (term, false)

let reducedTerm = mapTerm reduceMapping abstrTerm
mapTerm (fun x -> if x = reduceTerm.Value then (reducedTerm, true) else (x, false)) mainTerm
| _ -> mainTerm

/// <summary>
/// Normalizes term if possible based on normal reduction strategy
/// </summary>
/// <param name="term"> Term to normalize </param>
/// <returns> Transformed term </returns>
let rec normalize term =
let reduced = betaTransform term
if term = reduced then term else normalize reduced
12 changes: 12 additions & 0 deletions F#/LambdaInterpreter/LambdaInterpreter.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

<ItemGroup>
<Compile Include="LambdaInterpreter.fs" />
</ItemGroup>

</Project>
14 changes: 12 additions & 2 deletions F#/forSpbu.sln
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "PriorityQueue", "PriorityQu
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "PriorityQueue.Test", "PriorityQueue.Test\PriorityQueue.Test.fsproj", "{89A935E8-B5F3-435D-ACC3-A99DD7C66178}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "07.03", "07.03", "{5CA9053B-DE9B-4824-9F19-AEB464E3B9D8}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Homework1", "Homework1\Homework1.fsproj", "{04B15EE4-079A-42ED-ACC8-E2DCD25281C6}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "LambdaInterpreter", "LambdaInterpreter\LambdaInterpreter.fsproj", "{E83E0AE3-A385-497B-9F64-A9FD42A33D8E}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "LambdaInterpreter.Test", "LambdaInterpreter.Test\LambdaInterpreter.Test.fsproj", "{C2A83553-3EDE-4BA1-B185-9339BC42C2E0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -58,6 +60,14 @@ Global
{89A935E8-B5F3-435D-ACC3-A99DD7C66178}.Debug|Any CPU.Build.0 = Debug|Any CPU
{89A935E8-B5F3-435D-ACC3-A99DD7C66178}.Release|Any CPU.ActiveCfg = Release|Any CPU
{89A935E8-B5F3-435D-ACC3-A99DD7C66178}.Release|Any CPU.Build.0 = Release|Any CPU
{E83E0AE3-A385-497B-9F64-A9FD42A33D8E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E83E0AE3-A385-497B-9F64-A9FD42A33D8E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E83E0AE3-A385-497B-9F64-A9FD42A33D8E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E83E0AE3-A385-497B-9F64-A9FD42A33D8E}.Release|Any CPU.Build.0 = Release|Any CPU
{C2A83553-3EDE-4BA1-B185-9339BC42C2E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C2A83553-3EDE-4BA1-B185-9339BC42C2E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C2A83553-3EDE-4BA1-B185-9339BC42C2E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C2A83553-3EDE-4BA1-B185-9339BC42C2E0}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{7937CDA8-8285-4E23-AD1A-FC0F04FEEFE6} = {91E3BDA2-0836-46C2-95F0-02513FD7F13F}
Expand Down
6 changes: 0 additions & 6 deletions F#/forSpbu.sln.DotSettings.user

This file was deleted.