The issue here is you need types for the inner Tuples. So instead of this:
Tuple<Tuple, int, Tuple>
It has to look more like this:
Tuple<Tuple<Tuple, int Tuple>, int, Tuple<Tuple, int, Tuple>>
But now we've just pushed the problem down another level. We could in turn define those type arguments, but I think you get the idea that this problem never ends, and in fact grows exponentially with each new level.
Even worse, you can't just define a massive type with an arbitrarily sufficient number of levels to hold any practical tree. Instead, you'd have to define the type argument to exactly match the existing branches in your tree, or also define an overload for every possible size and balance level combination.
Therefore instead of Tuples you should use TreeNode:
class TreeNode<T>
{
public T Key{get; set;}
public TreeNode<T> Left{get; set;}
public TreeNode<T> Right{get; set;}
public TreeNode(T key)
{
this.Key = key;
}
}
public static TreeNode<T> TupleToBST<T>((TreeNode<T>, T, TreeNode<T>) tup)
{
if (tup == null) return null;
// Tuple items start counting at 1, not 0
return new TreeNode(tup.Item2) {Left = tup.Item1, Right = tup.Item3};
}
But I understand this is unlikely to satisfy you, because it seems like the goal is entering a single large expression into the code to create your data.
One option to consider is defining an implicit conversion from Tuple<TreeNode<T>, T, TreeNode<T>> to TreeNode<T>. I wasn't sure at first how the compiler would react to handling nested/recursive conversions, but it's an idea worth exploring.
But before we can go down this path we need to talk about the data. Even if this does work, the question has this example data:
( ( 1, 3, null), 2, ( ( null, 3, 4 ), 5, ( 6, 7, 8 ) ) )
This data would be invalid, because only the center item is allowed to have values. The left and right sides must always be either null or another Tuples/TreeNode, and every leaf node must always look like (null, value null). You would instead need to show it like this:
( ( (null, 1, null), 3, null), 2, ( ( null, 3, (null, 4, null) ), 5, ( (null, 6, null), 7, (null, 8, null) ) ) )
Now that we have valid data, we can try our conversion. And — amazingly — this seems to work:
public class TreeNode<T>
{
public T Key{get; set;}
public TreeNode<T> Left{get; set;}
public TreeNode<T> Right{get; set;}
public TreeNode(T key)
{
this.Key = key;
}
public static implicit operator TreeNode<T>((TreeNode<T>, T, TreeNode<T>) t) => new TreeNode<T>(t.Item2) {Left = t.Item1, Right=t.Item3};
}
public class Program
{
public static void Main()
{
TreeNode<int> tree = ( ( (null, 1, null), 3, null), 2, ( ( null, 3, (null, 4, null) ), 5, ( (null, 6, null), 7, (null, 8, null) ) ) );
Console.WriteLine(tree.Left.Key);
}
}
You can see it yourself here:
https://dotnetfiddle.net/7XaHa4
https://dotnetfiddle.net/ZvUPYM
Update:
I thought about it some more, and with these four total implicit conversion definitions we can also allow the original data expression to work:
(TreeNode<T>, T, TreeNode<T>)
(T, T, TreeNode<T>)
(TreeNode<T>, T, T)
(T, T, T)
And the class:
public class TreeNode<T>
{
public T Key{get; set;}
public TreeNode<T> Left{get; set;}
public TreeNode<T> Right{get; set;}
public TreeNode(T key)
{
this.Key = key;
}
public static implicit operator TreeNode<T>((TreeNode<T>, T, TreeNode<T>) t) => new TreeNode<T>(t.Item2) {Left = t.Item1, Right=t.Item3};
public static implicit operator TreeNode<T>((T, T, TreeNode<T>) t) => new TreeNode<T>(t.Item2) {Left = new TreeNode<T>(t.Item1), Right=t.Item3};
public static implicit operator TreeNode<T>((TreeNode<T>, T, T) t) => new TreeNode<T>(t.Item2) {Left = t.Item1, Right=new TreeNode<T>(t.Item3)};
public static implicit operator TreeNode<T>((T, T, T) t) => new TreeNode<T>(t.Item2) {Left = new TreeNode<T>(t.Item1), Right=new TreeNode<T>(t.Item3)};
}
public static void Main()
{
TreeNode<int> tree = ( ( 1, 3, null), 2, ( ( null, 3, 4 ), 5, ( 6, 7, 8 ) ) );
Console.WriteLine(tree.Left.Key);
}
See it work with the original expressions:
https://dotnetfiddle.net/mGN7Lf
https://dotnetfiddle.net/6I32WU
Now we can even (sort of) write the original method. The actual type for the argument is still TreeNode<T>, but we can pass the tuple in and get the TreeNode out:
public static TreeNode TupleToBST<T>(TreeNode<T> tup) //Line 36
{
return tup;
}
TreeNode<int> tree = TupleToBST<int>( ( ( 1, 3, null), 2, ( ( null, 3, 4 ), 5, ( 6, 7, 8 ) ) ));
https://dotnetfiddle.net/ni7jJf
Note the method doesn't really do anything... it was the implicit conversions and the compiler that did all the work here.